How to customize dynamic themes in Ant Design? This article introduces how to customize themes and implement dynamic themes in Ant Design. I hope it will be helpful to everyone!
Hey everyone, long time no see. I recently encountered the need for dynamic themes in the project, so I will record the implementation process. This article may be a bit long, please read it patiently
For convenience, the example in this article uses create-react-app caco craco-less
to implement:
# 创建项目 create-react-app demo # 安装必要依赖 yarn add antd # 记得要装最新版 yarn add -D @craco/craco craco-less babel-plugin-import
Modify npm-script
in package.json
:
{ scripts: { "start": "craco start" } }
By the way, add the initial craco. config.js
:
const CracoLessPlugin = require('craco-less'); module.exports = { plugins: [{ plugin: CracoLessPlugin }], };
Then, change App.js
:
import { useState } from 'react'; import { Avatar, Card, Button, Space, Switch } from 'antd'; function App() { const [theme, setTheme] = useState('light'); const checked = theme === 'light'; const handleThemeChange = (checked) => { setTheme(checked ? 'light' : 'dark'); }; return ( <div className="App"> <Card title={<Avatar size="large" src={logo} />}> <Space> <Switch checked={checked} checkedChildren="亮" unCheckedChildren="暗" onChange={handleThemeChange} /> <Button type="primary">动态主题</Button> </Space> </Card> </div> ); } export default App;
Then start and you will see the following interface:
3. How to introduce the theme? InAs for why there is no style, you can read the previous article "Can Vite satisfy you?" 》
##https://mp.weixin.qq.com/s/68E7CBXrhAd4u5kAt99nOA
Ant Design, there are many postures to introduce themes. Let’s briefly sort them out below.
1. Introduce style files
Introduce style files directly intoApp.js:
// App.js import 'antd/dist/antd.css';
craco-less is used, then use the
less file of Ant Design, but when you directly import
antd.less, you will get the following error:
javascriptEnabled in
lessOption Can:
const CracoLessPlugin = require('craco-less'); module.exports = { plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { javascriptEnabled: true, }, }, }, }, ], };
Ant Design Pro Application"]
Of course you can also choose to introduce it inApp.less:
@import '~antd/dist/antd.less';
2. babel-plugin-import
In the previous article, I talked about how to usebabel-plugin-import. The steps for using
craco are the same, but you need to use
craco.config Just add the corresponding configuration in .js:
// craco.config.js module.exports = { babel: { plugins: [ ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }], ], }, // ... };
App.js/App.less.
Ant Design styles. The following will explain how to modify and overwrite the original styles based on the above methods.
In fact, you can find that there are many style files provided inantd/dist:
├── antd.compact.less ├── antd.dark.less ├── antd.less ├── antd.variable.less ├── compact-theme.js ├── dark-theme.js ├── default-theme.js ├── theme.js └── variable-theme.js
antd.(dark|compact).less Two The two files are dark and compact mode styles respectively, and the
antd.variable.less file is a file only available in the latest version of
Ant Design. Its use will be discussed below. In addition to the .less style files, you will find that there are several
xxx-theme.js files. If you open them, you will find that they are actually the variable values of each theme, starting with
dark-theme.js For example:
const darkThemeSingle = { "theme": "dark", "popover-background": "#1f1f1f", "popover-customize-border-color": "#3a3a3a", "body-background": "@black", "component-background": "#141414", // ... };
1. Customized theme
If you want to implement a customized theme, it is very simple. Simply put, it isStyle Override, for example, when using a dark theme, directly introduce the dark mode style file. For example, if you want to modify the main color under the default theme, there are generally two ways: by modifying the style file or through lessOption.
// 1. 通过修改样式文件 // App.less @import '~antd/dist/antd.less'; @primary-color: green;
// 2. 通过 lessOptions // craco.config.js module.exports = { // ... plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { 'primary-color': 'cyan', }, javascriptEnabled: true, }, }, }, }, ], };
modifyVars The content of the settings. If you ask me why, then I will insert an extra lesson explained by
less
Less small class
Prerequisite: Introduceantd.less
in
App.lessand overwrite
@primary-color(take the
greenconfigured above as Example)
less provides a command line that allows us to escape the
.less file to
through the Terminal
command CSS:
# 转义 less 文件 npx lessc ./src/App.less ./src/App.css --js
primary-color is in
App.css:
.ant-btn-primary { border-color: green; background: green; }
可以看到 primary-color
设为 green
生效了,我们再加上 modifyVars
看下呢?
npx lessc ./src/App.less ./src/App.css --js --modify-var="primary-color: cyan"
在看下生成的 App.css
嘞:
.ant-btn-primary { border-color: cyan; background: cyan; }
Wow~竟然和我们本地开发时一样替换成了 modifyVars
中的内容!这又是为啥呢?
我们进入 node_modules/.bin/lessc
文件,在 parseLessFile
中 console
一下 data
和 options
内容会得到源文件字符串和命令行中的一些配置,在此我们会得到:
# data @import 'antd/dist/antd.less'; @primary-color: green; .App { text-align: center; } # options { javascriptEnabled: true, modifyVars: { 'primary-color': 'cyan' } }
随后我们再进入 node_modules/less/lib/less/render.js
文件,进入 render
方法可以看到:
var parseTree = new ParseTree(root, imports);
这一步是将 less
转为 AST
,让我们来看一下转换后的 AST
:
console.log(parseTree.root.rules); // Rules AST [ // ... Node { name: '@primary-color', value: Node { value: 'green' } }, Node { name: '@primary-color', value: Node { value: 'cyan' } }, // ... ]
这样是不是可以理解了?就是 modifyVars
中的变量覆盖了样式文件中的变量。下课!
再回到定制主题的相关内容,现在说下如何使用上面说到的 darkSingleTheme
,我们可以看下 theme.js
的内容:
function getThemeVariables(options = {}) { let themeVar = { 'hack': `true;@import "${require.resolve('antd/lib/style/color/colorPalette.less')}";`, ...defaultTheme }; if(options.dark) { themeVar = { ...themeVar, ...darkThemeSingle } } if(options.compact){ themeVar = { ...themeVar, ...compactThemeSingle } } return themeVar; }
可以看到,如果我们在使用 getThemeVariables
时将 dark
或 compact
设为 true
就能应用上对应的样式,我们来试下:
// craco.config.js module.exports = { // ... plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { ...getThemeVariables({ dark: true, }), }, javascriptEnabled: true, }, }, }, }, ], };
就是这么简单,在使用 getThemeVariables
时也可以搭配前面所说的 modifyVars
来覆盖样式。这就是常用的定制主题的方式,就是之前所说的 覆盖变量,总结一下这两种方式:
引入主题样式文件并覆盖对应 Less
变量;
使用 getThemeVariables
引入对应主题,通过 modifyVars
覆盖变量值;
2. 动态主题
到现在 Ant Design 文档都没有出切换亮/暗模式的功能,但在文档中却有提到相应功能。在本文中主要介绍3种方式,其中一种就是官方出的 动态主题(实验性)。
I. 动态切换样式文件
切换样式文件,这应该是最容易想到的一个方案,Ant Design
本来就提供了例如 default/dark/compact
主题的样式,我们只需要将这些文件保存在我们的项目中,按需切换即可,这个方案不赘述,实现起来也十分简单。
II. ConfigProvider
ConfigProvider
是 Ant Design
提供的一个实验性的动态主题方案,使用很简单,在入口 .less
文件中引入 variable.less
文件,然后在 ConfigProvider
中复写对应变量,具体使用如下:
// App.less @import 'antd/dist/antd.variable.less';
默认样式与 primary
一样,然后我们使用 ConfigProvider
修改主题色(还是以 primaryColor
为例):
// App.js // 增加下面一行 ConfigProvider.config({ theme: { primaryColor: 'aquamarine' } });
动态切换我们与上面使用方式一致:
// App.js ConfigProvider.config({ theme: { primaryColor: checked ? 'aquamarine' : 'darkgreen', }, });
这么方便,这么好用,他有什么不足之处么?有,但只要你不介意其实问题不大。通过 ConfigProvider
可以配置的颜色很有限:
// node_modules/antd/es/config-provider/context.d.ts interface Theme { primaryColor?: string; infoColor?: string; successColor?: string; processingColor?: string; errorColor?: string; warningColor?: string; }
可以看到,通过这种方式来配置的颜色仅有上面六种,但如果你想 extends Theme
来添加其他字段,那不好意思,行不通,再来看下它是如何处理这几种颜色的:
/** * @param {string} colorVal * @param {string} type */ var fillColor = function fillColor(colorVal, type) { var baseColor = new TinyColor(colorVal); var colorPalettes = generate(baseColor.toRgbString()); variables["".concat(type, "-color")] = formatColor(baseColor); variables["".concat(type, "-color-disabled")] = colorPalettes[1]; variables["".concat(type, "-color-hover")] = colorPalettes[4]; variables["".concat(type, "-color-active")] = colorPalettes[7]; variables["".concat(type, "-color-outline")] = baseColor.clone().setAlpha(0.2).toRgbString(); variables["".concat(type, "-color-deprecated-bg")] = colorPalettes[1]; variables["".concat(type, "-color-deprecated-border")] = colorPalettes[3]; }; // 使用如下 fillColor(theme.successColor, 'success');
所以他只是对这几种颜色进行了特定处理,而对于其它的颜色(比如组件背景色)等并未作处理,但即使某些颜色的命名方式也符合这种规范,也不会奏效,毕竟 Ant Design
使用了 if (theme.successColor)
这种方式来条件修改这些颜色。
III. CSS Variables
我打算在这一部分来介绍 II. ConfigProvider 中 antd.variable.less
的内容,因为这个方法与 Ant Design
提供的 ConfigProvider
本质上有些类似:通过 CSS Variables
来修改全局的颜色。我们打开对应文件来简单看下内容:
// node_modules/antd/lib/style/themes/variable.less html { @base-primary: @blue-6; --@{ant-prefix}-primary-color: @base-primary; --@{ant-prefix}-primary-color-hover: color(~`colorPalette('@{base-primary}', 5) `); --@{ant-prefix}-primary-color-active: color(~`colorPalette('@{base-primary}', 7) `); --@{ant-prefix}-primary-color-outline: fade(@base-primary, @outline-fade); // ... }
上面的代码中涉及了 less
中几个比较基本的语法:Variable Interpolation 和 Escaping。
具体内容可以看上面截图,我就不再赘述,Ant Design
就通过这种方式实现了动态主题,将 color
值设置为可变的 CSS Variables
。既然 ConfigProvider
可变的 color
有限,那我们就自己来定义这些颜色吧~这种方式实现起来比较复杂,但是可自定义性就无限可能了
// App.less :root { --main-color: green; } .ant-btn { &.ant-btn-primary { border-color: ~'var(--main-color)'; background: ~'var(--main-color)'; } }
看一下通过这种方式实现的效果:
如何实现修改 :root
中的样式呢?具体 Ant Design
的实现各位可以看 node_modules/antd/es/config-provider/cssVariable.js
和 node_modules/rc-util/es/Dom/dynamicCSS.js
两个文件,内容十分简单。我先写了一个简易版:
const dark = { '--main-color': 'darkgray', }; const light = { '--main-color': 'green', }; const themes = { dark, light }; const changeTheme = (theme) => { const nextTheme = themes[theme]; Object.keys(nextTheme).forEach((key) => { document.documentElement.style.setProperty(key, nextTheme[key]); }); };
changeTheme
方法就是修改 :root
中样式的方法。
但为什么不直接在 App.less
中采用 @primary-color: ~'var(--main-color)'
的形式,非要重写组件样式呢?
如果你去看 Ant Design
中样式文件的源码你会发现其中用到了很多 function
,比如 less
中的 fade
函数:
Set the absolute opacity of a color. Can be applied to colors whether they already have an opacity value or not.
来自 Less 官网:https://lesscss.org/functions/#color-operations-fade
如果我们采用刚才说的那种形式来修改 @primary-color
等样式,less
就会抛出异常:Argument cannot be evaluated to a color
:
// node_modules/less/lib/less/functions/color.js // fade 方法 function fade (color, amount) { var hsl = toHSL(color); hsl.a = amount.value / 100; hsl.a = clamp(hsl.a); return hsla(color, hsl); } // toHSL 方法 function toHSL(color) { // 此处的 color.toHSL 函数是下面 color.js 中的 toHSL 函数 if (color.toHSL) { return color.toHSL(); } else { throw new Error('Argument cannot be evaluated to a color'); } } // node_modules/less/lib/less/tree/color.js function toHSL () { var r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha; // ... },
这样就可以看出如果我们传给 fade
函数的不是一个准确的颜色值,在 color.js
中是获取不到 rgb[0]
等值的,所以在 less
编译过程中就会直接报错。
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of How to customize dynamic themes in Ant Design? Let's talk about implementation methods. For more information, please follow other related articles on the PHP Chinese website!