With the increasing demand for embedding Ali data pages, some demanding business parties have emerged, hoping to specify the theme color of Ali data to be consistent with their brand color and make the product have a better experience. So we started the research on the dynamic theme function. We thought it was a relatively simple task, but actually there were some twists and turns. However, the final result was quite satisfactory, which met our needs, and was very simple and lightweight.
I. Existing programs
Before starting, I investigated a wave of alternative schemes. The existing schemes are generally of two types: one is that there are multiple preset themes at the time of compilation and construction, only style switching is done at runtime, and a new theme needs to be rebuilt and released; The other is that the runtime can specify any topic, and adding a new class of topics costs almost nothing and is easy to expand.
1. Compile multiple preset themes
The Class switch
An example of compiled CSS is shown below, which switches the theme by giving the body a theme-light or theme-dark class. Less/Sass can be used to facilitate batch processing, but the disadvantages are obvious: CSS file sizes can double.
// index.css
.theme-light div {
color: # 000;
}
.theme-dark div {
color: #fff;
}
Copy the code
Build multiple CSS
The drawbacks of class switching can be solved by building multiple CSS files, such as our less code:
div {
color: @primary-color;
}
Copy the code
The construction phase then specifies @primary-color as a different color value to get multiple CSS artifacts:
// index-light.css
.div {
color: # 000;
}
Copy the code
// index-dark.css
div {
color: #fff;
}
Copy the code
CSS and index-dark. CSS are toggled during runtime. But this solution is also unfriendly, requiring intrusion into the build configuration, which then increases the build time.
In the compilation stage preset more than a theme of the scheme, the implementation is relatively simple, the principle is also very well understood, but there are obvious disadvantages, and expand the new theme will have costs.
2. Specify any topic at run time
If you want to extend the theme inexpensively, you must leverage the capabilities of the runtime, which can be implemented in one of three ways, from difficult to easy:
CSS template + runtime replacement
Elemental-ui uses this solution. When switching colors, it passes the color value to the back end to create a new CSS file:
Of course, the work of pure front-end can also be done, the program is quite good.
Less variable
This solution uses the runtime capabilities of less. Js to achieve similar effects to the previous solution, where the CSS template becomes a less file and the runtime substitution is provided by modifyVars of less. It looks something like this:
- HTML main document adds the project’s LESS file and
less.js
<link rel="stylesheet/less" type="text/css" href="/styles.less" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
Copy the code
- use
modifyVars
Modify color variables
window.less.modifyVars({
'@primary-color': '#0035ff'
})
Copy the code
This scheme has many disadvantages. One is to introduce 40KB less-.js runtime; The second is that coding and packaging are intrusive. We need to add all less files to HTML. Third, theme switching is not smooth, because modifyVars involves recompiling less files.
CSS variable
CSS variables are a new feature of the CSS3 standard, and it is easy to make themes with them. For example, here are our styles:
div{// use --primary-colorThe color of the variable, default if no value# 000
color: var(--primary-color, # 000);
}
Copy the code
To switch themes, simply set the new color value through the Document API
document.body.style.setProperty('--primary-color'.'#fff')
Copy the code
This feels like the simplest solution of all, powerful and easy to understand.
There is also a similar STYLEShad-Components CSS in JS scheme, but it changes the project too much, and specifying the theme of the ANTD component would be cumbersome and can be ignored.
Second, our choice
Compiling multiple preset theme types was first killed by us. We have high requirements for low-cost development, otherwise if the business side changes the theme color, we have to follow the release.
The CSS variable scheme has the lowest cost and:
- Mainstream browsers are supported, and 99%+ of our users are Chrome, so compatibility can be ignored.
- And then we’ve used less ourselves, by putting
@primary: #ff6a00
to@primary: var(--primary-color, #ff6a00)
It is easy to use and does not require a lot of changes to existing LESS files in the project. - We found that ANTD already supports CSS variables to dynamically specify themes;
Perfect, so we ended up with the CSS variable scheme.
Three, hands-on practice
1. Modify global.less
Change the value of the less variable to the CSS variable, and place the original value into the default value
- @primary: #ff6a00;
- @primary5: #ff6a000d;
- @primary15: #ff6a0026;
- @primary75: #ff6a00BF;
+ @primary: var(--primary-color, #ff6a00);
+ @primary5: var(--primary-color-5, #ff6a000d);
+ @primary15: var(--primary-color-15, #ff6a0026);
+ @primary75: var(--primary-color-75, #ff6a00BF);
Copy the code
Local run, no problem, color is normal
2. Modify CSS variables
We added a theme. Ts file that executes its setupTheme in the entry to use the theme color specified on the URL argument.
// theme.ts
import { getUrlParams } from '@/utils/utils';
export let primaryColor = '#ff6a00';
export let primaryColor5 = '#ff6a000d';
export let primaryColor15 = '#ff6a0026';
export let primaryColor75 = '#ff6a00BF';
export function setupTheme() {
const params = getUrlParams() as any;
if(params? .primaryColor) { primaryColor =` #${params? .primaryColor.toLocaleLowerCase()}`;
primaryColor5 = `${primaryColor}0d`;
primaryColor15 = `${primaryColor}25 `;
primaryColor75 = `${primaryColor}BF`;
}
document.body.style? .setProperty('--primary-color', primaryColor);
document.body.style? .setProperty('--primary-color-5', primaryColor5);
document.body.style? .setProperty('--primary-color-15', primaryColor15);
document.body.style? .setProperty('--primary-color-75', primaryColor75);
}
Copy the code
Local run, specify the primaryColor parameter to other color values, except antD series components are already in effect
3. Specify the ANTD component theme
Follow documentation guidelines, we upgrade the antd 4.17.1 – alpha. 1 version of the import antd/dist/antd variable. Min. CSS, then in the theme. The ts in new topic ConfigProvider to set the color, refresh the page, Antd series components are also in effect, perfect! Package to pre-send feel under…
Only part of antD component themes took effect, and some components, such as pager, did not take effect. Debugging found that ANTD styles were redundant, and CSS variable versions of some components were overwritten. Our UMi.css looks like this:
// The style of the CSS variable version is overwritten by the following, causing the specified theme color to not take effect.ant-pagination-item-active {
border-color: var(--ant-primary-color);
}
.ant-pagination-item-active {
border-color: #ff6a00;
}
Copy the code
My first reaction was to modify the CSS order and put the styles of CSS variable version behind. After a lot of trouble, I found that the CSS order of Webpack was not the same as the order of import. Finally, when I saw the issue, I decided to give up.
4. Turn off antD loading on demand
As written in antD’s documentation, ANTD dynamic themes need to be closed for loading on demand:
Note: If you use babel-plugin-import, you need to remove it.
It seems to be the only way. According to the UMI document, @umijs/ plugin-ANTd will introduce antD on demand. After looking through the source code, it cannot be configured and closed at present, so we proposed a PR and talked with Shixian privately.
Then we update @umijs/ preth-react to add disableBabelPluginImport in.umirc.ts to disable on-demand loading.
antd: {
disableBabelPluginImport: true
}
Copy the code
Should be stable, sent to pre-send to feel again…
The pager is fine, but the button is broken. It is found that the CSS variable styles are overwritten due to redundant styles. It is finally traced to @ant-Design/Pro-Layout
5. Specify a Pro-Layout theme
Reason is pro – layou have reference lib | es less files in the directory, press antd document, need injection in less @ root – an entry – name: variable variables in. Umirc. Ts configured in the following:
theme: {
'root-entry-name': 'variable'
}
Copy the code
Once again to pre-send, OK, all ready!
Note that ‘primary-color’ cannot be configured after ‘root-entry-name’: ‘variable’ is configured in the theme
Four,
It can be seen that the dynamic theme scheme based on CSS variables is relatively simple. For the projects already using less, the access cost is very low, and the scheme is light and easy to expand. Antd has also given official support to further reduce the use cost, and I believe that it will become the choice of more people in the future.
Develop reading: variables CSS tutorial: www.ruanyifeng.com/blog/2017/0… Element-ui skin remover: github.com/ElemeFE/ele… Talk about the front-end avi: segmentfault.com/a/119000001…