Skip to main content
Version: 3.x

主题

我们基于 Ant Design 视觉风格搭建了 Godzilla Design,这套风格经过设计师的精心调配,适合大多数中后台项目。如果对视觉风格有额外的要求,推荐使用以下的方式进行定制。

一、主题开发配置

// 在quantex-scaffold项目的配置文件config/config.ts中
themeConfig: {
// 指定默认主题: 对应themes中的id
defaultTheme: 'themeDark',
mainTheme: 'themeDark',
// 主题配置
themes: [
{
// 名称
name: '深色',
// id
id: 'themeDark',
// 主题文件路径
path: path.join(__dirname, '../packages/quantex-plugin-theme-dark'),
// chart主题配置文件
chartConfig: darkChart,
},
{
name: '浅色',
id: 'themeWhite',
path: path.join(__dirname, '../packages/quantex-plugin-theme-white'),
chartConfig: whiteChart,
},
],
},

二、实现原理

背景

基于 Avatar 组件规范 V2.0 进行组件主题开发。

  1. quantex-design 是基于 ant-design 的二次封装;
  2. ant-design 使用的是 less 方式 处理 css;
  3. quantex-scaffold 使用的是 sass 方式处理 css;
  4. quantex-scaffold 同时会依赖 ant-design css;
  5. css variables 一定程度上吸收了 less/sass 处理 css 的方式,且主流浏览器都已支持;
  6. 因此在点击主题切换按钮时,quantex-design 和 ant-design 所有的组件样式都需要进行切换。
  7. 为减少代码量,同时可以复用部分代码。决定使用 css variables + 样式覆盖的方案进行主题切换的实现。
  8. 样式覆盖:需要针对所有组件的所有样式进行覆盖。
  9. css variables: 样式覆盖使用的样式需要使用 css variables 进行定义。
  10. 因此在 build 完成之后,会生成组件 css 文件 + css variables 文件;主题切换加载不同 css variables 文件即可。

原理

  1. 使用 css variales 对组件样式进行覆盖;
  2. build 生成组件样式文件和 css variables 文件;
  3. 组件样式使用的 css 变量都定义在统一的 css variables 文件中;
  4. 切换主题即加载不同 css variables 文件;
  5. 同时自定义主题只需要根据现有 css variables 文件进行修改;

三、变量定义及使用

变量定义:

packages/quantex-plugin-theme-dark/index.scss
:root {
// ====Avatar 2.0 设计规范====
// common layout background
--common-layout-bg: #373e47;
// component background used for content level
--component-bg: rgba(19, 21, 24, 1); // 内容层
--body-bg: #000000; // body bg

// ===字体颜色===
--text-color-1: rgba(242, 242, 242, 1); // lighter color
--text-color-2: rgba(242, 242, 242, 0.65); // main color
--text-color-3: rgba(242, 242, 242, 0.3); // weak color
--text-color-4: rgba(242, 242, 242, 0.2); // weaker color

--common-disabled-color: rgba(242, 242, 242, 0.2); // disabled color

--common-border-color: #bdbdbd; // common border color

--common-transition: all 0.3s ease; // common transition

--common-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.6); // common box-shadow

// ===主题色===
--primary-color: #006ab5; // 主题色
--primary-hover-color: #0080da; // 主题色
--success-color: #5ec778; // 成功色
--warning-color: #f1963a; // 警告色
--error-color: #e33b50; // 危险色
--white-color: #ffffff; // 白色
--light-color: rgba(242, 242, 242, 1); // 白色
--normal-color: rgba(242, 242, 242, 0.65); // 白色

// ==== Text ====
--text-selection-color: var(--white-color); // 鼠标选中字体颜色
--text-selection-bg: var(--primary-color); // 鼠标选中背景颜色

// ====Layout====
--layout-sider-bg: #111111; // 侧边栏
--layout-header-bg: var(--common-layout-bg); // 顶部
--layout-body-bg: var(--component-bg); // body
--layout-footer-bg: var(--common-layout-bg); // 底部
--layout-nav-bg: var(--common-layout-bg); // nav
--layout-nav-tab-bg: var(--common-layout-bg); // nav tab
--layout-nav-tab-active-bg: #5b616e; // nav tab active bg
--layout-nav-tab-hover-bg: rgba(39, 43, 50, 1); // nav tab hover bg
--layout-sider-tab-bg: #373e47; // sider tab
--layout-sider-tab-active-bg: #5b616e; // sider tab active bg
--layout-sider-tab-hover-bg: rgba(39, 43, 50, 1); // sider tab hover bg
--layout-container-border-color: rgba(19, 21, 24, 1); // layout container border color
--footer-collapse-bg: rgba(39, 43, 50, 1); // footer collapse bg

// ====Button====
--btn-primary-bg: var(--primary-color); // btn primary bg
--btn-primary-hover-bg: rgba(0, 128, 218, 1); // btn primary hover bg
--btn-primary-active-bg: rgba(0, 85, 145, 1); // btn primary active bg

--btn-danger-normal-bg: var(--error-color); // btn danger normal bg
--btn-danger-bg: var(--error-color); // btn danger color
--btn-danger-hover-bg: rgba(244, 75, 96, 1); // btn danger hover color
--btn-danger-active-bg: rgba(180, 38, 56, 1); // btn danger active color

--btn-default-bg: rgba(55, 62, 71, 1); // btn default background
--btn-default-hover-bg: rgba(91, 97, 110, 1); // btn default background
--btn-default-active-bg: rgba(39, 43, 50, 1); // btn default active background

--btn-disable-bg: rgba(55, 62, 71, 1); // btn disable background
--btn-disable-color: var(--common-disabled-color); // btn disable color

// ====Input====
--input-bg: rgba(41, 44, 48, 1); // input背景色
--input-border-color: var(--input-bg); // input border color
--input-focus-border-color: rgba(0, 106, 181, 1);
--input-placeholder-color: var(--text-color-3); // input placeholder color
--input-addon-bg: rgba(55, 62, 71, 1); // input addon bg
--input-affix-color: rgba(242, 242, 242, 0.3); // icon color
--input-affix-hover-color: rgba(242, 242, 242, 1); // input affix hover color
--input-disabled-bg: #1a1c20; // input disabled bg
--input-disabled-color: var(--common-disabled-color); // input disabled color

// ====Radio====
--radio-group-item-hover-bg: rgba(55, 62, 71, 1); // radio-group-item-hover-bg
--radio-group-button-bg: rgba(39, 43, 50, 1); // radio group button bg

// ====Checkbox====
--checkbox-bg: var(--component-bg); // checkbox bg
--checkbox-disabled-bg: rgba(32, 35, 39, 1); // checkbox disabled bg

// ====Select====
--select-bg: rgba(41, 44, 48, 1); // select bg
--select-dropdown-bg: rgba(27, 29, 33, 1); // select-dropdown-bg
--select-dropdown-item-selected-bg: rgba(19, 21, 24, 1); // select-dropdown-item-selected-bg
--select-item-hover-bg: rgba(41, 44, 48, 1); // select dropdown item hover bg
--select-item-active-bg: rgba(41, 44, 48, 1); // select dropdown item active bg
--select-selection-item-bg: rgba(55, 62, 71, 1); // select selector item bg

--select-tree-node-hover-bg: rgba(41, 44, 48, 1); // tree select node hover bg
--select-tree-node-selected-bg: #131518; // tree select node selected bg
--select-tree-list-holder-bg: var(--tree-bg); // tree select list bg

// ====Workbench====
--workbench-list-select-bg: rgba(25, 26, 30, 1); // workbench list select bg
// ====Menu====
--menu-dark-bg: rgba(17, 17, 17, 1); // menu dark bg
--menu-submenu-dark-bg: rgba(25, 26, 30, 1); // menu submenu dark bg
--menu-item-hover-color: var(--white-color); // menu item hover color
// ====Table====
--table-item-border-color: rgba(39, 43, 50, 1); // table-item-border-color
// ====Dropdown====
--dropdown-menu-bg: rgba(27, 29, 33, 1); // dropdown-menu-bg
--dropdown-item-active-bg: rgba(41, 44, 48, 1); // dropdown-item-active-bg
// ====Cascader====
--cascader-bg: var(--select-bg); // cascader bg
--cascader-menu-bg: rgba(27, 29, 33, 1); // cascader menu bg
--cascader-item-selected-bg: var(--component-bg); // cascader-item-selected-bg
--cascader-item-hover-bg: var(--select-tree-node-hover-bg); // cascader-item-hover-bg
--cascader-menu-item-disabled-bg: var(--input-disabled-bg);

// ====TimePicker DatePicker====
--picker-bg: #202327; // picker input bg
--picker-panel-bg: rgba(27, 29, 33, 1); // picker panel bg
--picker-border-color: var(--component-bg); // picker border bg
--picker-hover-bg: rgba(41, 44, 48, 1); // time/date picker hover bg
--picker-active-bg: rgba(41, 44, 48, 1); // time/date picker active/focus bg
--time-picker-panel-item-selected-bg: rgba(19, 21, 24, 1); // time picker panel item selected bg

// ====Switch====
--switch-closed-bg: var(--common-layout-bg); // switch closed bg

// ====Modal====
--modal-bg: rgba(27, 29, 33, 1); // Modal 背景色
--modal-content-bg: var(--modal-bg); // Modal content背景色
--modal-footer-bg: var(--modal-bg); // Modal footer 背景色
--modal-border-color: var(--component-bg); // modal border color

// ====Alert====
--alert-bg: rgba(27, 29, 33, 1); // Alert 背景色
--alert-box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.6); // alert box-shadow

// ====Popover prompt popconfirm====
--popconfirm-bg: rgba(27, 29, 33, 1); // Popconfirm 背景色
--popconfirm-shadow-color: rgba(0, 0, 0, 0.6); // Popconfirm shadow color
--popover-bg: var(--popconfirm-bg); // popover-bg

// ====Transfer====
--transfer-item-hover-color: rgba(41, 44, 48, 1); // transfer item hover color
--transfer-item-selected-color: rgba(19, 21, 24, 1); // transfer item selected color
--transfer-bg: rgba(27, 29, 33, 1); // transfer bg
--transfer-header-border-color: var(--body-bg); // transfer header bottom border color

// ====Upload====
--upload-list-item-hover-bg: rgba(27, 29, 33, 1); // upload list item hover bg
--upload-drag-bg: rgba(27, 29, 33, 1); // upload draggable bg
--upload-drag-border-color: var(--common-disabled-color); // upload draggable border color

// ====Slider====
--slider-handle-bg: #000000; // slider handle bg
// ====Progress====
--progress-inner-bg: rgba(242, 242, 242, 0.2); // progress inner bg
// ====Pagination====
--pagination-item-bg: rgba(41, 44, 48, 1); // pagination-item-bg
--pagination-light-color: rgba(242, 242, 242, 1); // light-color
// ====Rate====
--rate-color: rgba(255, 191, 0, 1); // rate color
--rate-star-second-color: rgba(27, 29, 33, 1); // rate start second color
// ====Tooltip====
--tooltip-shadow-color: rgba(0, 0, 0, 0.6); // tooltip shadow color
// ====Tag====
// tag color
--tag-magenta-color: rgba(234, 85, 127, 1);
--tag-red-color: rgba(227, 59, 80, 1);
--tag-volcano-color: rgba(241, 101, 73, 1);
--tag-orange-color: rgba(241, 150, 58, 1);
--tag-gold-color: rgba(241, 187, 58, 1);
--tag-lime-color: rgba(94, 199, 120, 1);
--tag-green-color: rgba(10, 179, 156, 1);
--tag-cyan-color: rgba(15, 152, 173, 1);
--tag-blue-color: rgba(0, 106, 181, 1);
--tag-geekblue-color: rgba(80, 115, 184, 1);
--tag-purple-color: rgba(161, 102, 171, 1);
// tag bg
--tag-magenta-bg: rgba(234, 85, 127, 0.2);
--tag-red-bg: rgba(227, 59, 80, 0.2);
--tag-volcano-bg: rgba(241, 101, 73, 0.2);
--tag-orange-bg: rgba(241, 150, 58, 0.2);
--tag-gold-bg: rgba(241, 187, 58, 0.2);
--tag-lime-bg: rgba(94, 199, 120, 0.2);
--tag-green-bg: rgba(10, 179, 156, 0.2);
--tag-cyan-bg: rgba(15, 152, 173, 0.2);
--tag-blue-bg: rgba(0, 106, 181, 0.2);
--tag-geekblue-bg: rgba(80, 115, 184, 0.2);
--tag-purple-bg: rgba(161, 102, 171, 0.2);
--tag-success-bg: rgba(94, 199, 120, 0.2);
--tag-processing-bg: rgba(0, 106, 181, 0.2);
--tag-error-bg: rgba(227, 59, 80, 0.2);
--tag-warning-bg: rgba(241, 150, 58, 0.2);
// ====Tab====
--tab-bg: rgba(55, 62, 71, 1); // tab bg
--tab-border-color: transparent; // tab border color

// ====Disabled====
--disabled-bg: #242427; // 禁止使用组件背景色

// ag-grid
--ag-grid-panel-bg: #1b1d21; // ag-grid panel bg
--ag-grid-panel-drop-list-bg: #292c30; // ag-gird panel drop list bg
--ag-border-color: rgba(39, 43, 50, 1); // ag-border-color
// ====Avatar====
--avatar-bg: rgba(242, 242, 242, 0.65); // avatar bg

// 涉及具体业务
--quick-search-bg: rgba(41, 44, 48, 1); // quick search bg

// 股票涨跌:红色、绿色
--stock-up-color: #f04134;
--stock-down-color: #3dbd7d;
}

变量使用:

// app/styles/core/_variables.scss
// 在scss 文件中使用 css variables 定义 scss变量
// primary color
$button-primary-text-color: var(--light-color);

// app/styles/themes/Display/AntAvatar.scss
// 在scss文件中直接使用css variables 覆盖样式
.ant-avatar {
background-color: var(--avatar-bg);
.anticon {
color: var(--white-color);
}
}

四、定制自己的主题

基于主题切换的实现原理,自定义主题只需要根据现有 css variables 文件进行修改。

目前 Godzilla UI 有两套默认主题,我们计划在之后推出更多主题满足个性化的需求,敬请期待。

五、使用useTheme获取实时主题

godzilla 最低版本 >= v3.4.2

// 引入 useTheme hook
import useTheme from '@/hooks/useTheme';


// 使用 useTheme hook
const [currentTheme, setCurrentTheme] = useTheme();
// 正常情况下不需要使用setCurrentTheme
// currentTheme 将跟随系统自动更新,会保持为最新的主题
const [currentTheme, _] = useTheme();