There are two dynamic peels that I know of, one is by modifying the less variable with less.modifyVars, and the other is by using var CSS, which is not supported by many browsers. Moreover, the main scene of our project lies in the mobile terminal, so we can only choose to implement the scheme through less.

The principle is briefly

In fact, the principle is easy to understand. Extract the CSS class that contains less variables, regenerate the CSS class by modifying the value of the variable, and then add it to the DOM. As in project code, write the following style:

@abcd-efg : #f3574c;

.center {
  color: @abcd-efg;
  font-size: 26px;
  height: 50px;
}
Copy the code

After compiling in the framework, it produces CSS like this (e.g. Umi.css):

.center {
  color: #f3574c;
  font-size: 26px;
  height: 50px;
}
Copy the code

In normal scenarios where dynamic peels are not required, this is the CSS style we ultimately need. If dynamic peels are required, we can keep the less variables and create a new LESS file (assuming the name is alita.less).

.center {
  color: #f3574c;
  font-size: 26px;
  height: 50px;
}
Copy the code
@abcd-efg : #f3574c;

.center {
  color: @abcd-efg;
}
Copy the code

Less. Js then converts the less file into a CSS file and puts it on the DOM.

The final HTML file we deployed looks like this:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <link rel="stylesheet" href="/umi.css" />
</head>
<body>
  <link rel="stylesheet/less" type="text/css" href="/alita.less" />
  <script src="less.js"></script>
</body>
</html>
Copy the code

When the user accesses the page, on the browser side, less. Js compiles the LESS file in the DOM to CSS style, and the HTML changes are roughly as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <link rel="stylesheet" href="/umi.css" />
  <! Center {color: #f3574c; font-size: 26px; height: 50px; } -- >
</head>
<body>
  <link rel="stylesheet/less" type="text/css" href="/alita.less" />
  <style type="text/css" id="less:alita">
  /* The less file above is compiled into */
  .center {
    color: #f3574c;
  }
  </style>
  <script src="less.js"></script>
</body>
</html>
Copy the code

When we modify variables in the project using less.modifyVars, the less file is recompiled.

window.less.modifyVars({
  'abcd-efg': '#0000FF'
})
Copy the code
  <style type="text/css" id="less:alita">
  /* Here is the regenerated CSS style */
  .center {
    color: #0000FF;
  }
  </style>
Copy the code

Then you understand the principle, and the next step is implementation.

implementation

Extract variable

Antd Runtime Theme Update #10007 (AntD Runtime Theme Update #10007, AntD Runtime Theme Generator) The directory path and variable name of ANTD have been written in the source code, but basically all the methods provided can be used, because WHAT I need is the solution of mobile terminal, that is, ANTD-mobile. Fortunately, the structure of antd-mobile@2 is basically the same as antD. After simple modification, You can use it.

However, a problem was found that variable modification could only modify variables in antD-Mobile components, and could not change variable names used in the project.

import { Button } from 'antd';
import styles from './index.less';
// index.less
// @import '~antd-mobile/lib/style/themes/default.less';

// .center {
// .primary {
// color: @brand-primary
/ /}
// }

const Page: FC<PageProps> = () = > {
  return (
    <div className={styles.center} >
      <Button type="primary" >button</Button>
      <span className={styles.primary}>The color here uses the theme color variable in the less file. @brand-primary</span>
    </div>
  );
}
Copy the code

When you change the variables, you find that buttons and other components that use theme colors change, but not classes that use the same variables in the project.

window.less.modifyVars({
  'brand-primary': '#FF0000'
})
Copy the code

The first guess is that the custom less file was not compiled. After checking the final product, it is found that the file read by ANTD-Theme-generator compilation is the original file. However, after the CSS module is used in UMI project, the class name will be suffixed by default in CSS-Loader. The resulting less compiled class is named.center while the real class is named.center__kjahd.

Because in the UMI life cycle, there is no time to obtain files with less variables and class name suffixes. Therefore, the CSS file compiled by UMI was read directly from the Webpack construction link. All CSS files are taken in consideration of the possibility of loading on demand.

class UmiThemePlugin {
  apply(compiler) {
    const options = this.options;
    compiler.hooks.emit.tapAsync('UmiThemePlugin'.(compilation, callback) = > {
      options.customCss = ' ';
      Object.keys(compilation.assets).map((i) = > {
        if (i.endsWith(".css")) {
          options.customCss = `${options.customCss}\n${compilation.assets[i].source()}`} }) generateTheme(options) }); }}module.exports = UmiThemePlugin;

Copy the code

Since less to CSS is compiled by UMI, there is no need for secondary compilation in ANTD-theme-generator. So simply remove the content of the compiled CSS file.

Umi plug-in

In line with the principle that the more you do in the framework, the less you do in the project delivery, manually imported files required in antD-Theme-generator documentation and other matters needing attention are implemented in the form of UMI plug-ins. Finally, @alitajs/plugin-theme is completed. After installation, configure the following theme in the configuration file:

  plugins:['@alitajs/plugin-theme'].dynamicTheme: {type:'antd-mobile'.varFile: path.join(__dirname, '.. /src/default.less'),
    themeVariables: ['@brand-primary'.'@abcd-efg'],}Copy the code
attribute instructions
type Declare whether it is ANTD or ANTD-Mobile, and the packet path will be found automatically
varFile Statement less variable file path, did not provide, will default to find ‘style/themes/default. The less’
themeVariables The names of variables to be extracted must be displayed and specified before they can be used when changing variables. The more variables to be modified, the larger the generated LESS file is

Remaining issues

  1. Less Version Problems

    Importing [email protected] with window.less. ModifyVars is possible. However, less@3 is used in the project, using import less from less; Less. ModifyVars is disabled even if javascriptEnabled is set to true.

    Related Issues github.com/mzohaibqc/a…

  2. The value of less variable must be unique

    Since the CSS used is a file compiled by UMI, the less variable is not recorded in the middle, and the subsequent actions are reversed binding by value matching.

    The disadvantage is that if both variable names specify the same color value, they will eventually be combined into one.

    The advantage is that even if you forget to use variables when writing color values in the project, you can also achieve dynamic skin, which is a great benefit for the functional follow-up of legacy projects.

Applicable items

Theoretically all umi system projects can be used, such as UMI, Dumi, Ant-Design-Pro, Alita, etc. Currently, umi, Alita and Ant-Design-Pro projects are tested.

Test Ant-Design-Pro with your eyes closed

  1. Pull the current V5 branch code
  2. yarn add @alitajs/plugin-theme
  3. Added the configuration in config/config.ts
  4. SRC/pages/User/login/index. The TSX, literally wrote a button

config/config.ts

export default defineConfig({
+ plugins: ['@alitajs/plugin-theme'],
+ dynamicTheme: {
+ type: 'antd',
+ themeVariables: ['@layout-body-background'],
+},
});
Copy the code

src/pages/User/login/index.tsx

+ 
+ window.less.modifyVars({
+ 'layout-body-background': '#FF0000'
+})
+}}> Click background color change 
Copy the code

Random effect

conclusion

My hydrology always can not lack the summary, this plan is quite interesting, when running the plan, found a lot of interesting problems, when writing the article, I think it is quite simple. It took a total of four days from the beginning of the project to the final availability of the solution, and three new packages were delivered.

Welcome to try, welcome to discuss.

The source code

[umi plugin] : github.com/alitajs/plu…

[Generate less files from umi. CSS] : github.com/alitajs/umi…

[Webpack plugin, main function is to fetch umi. CSS file] : github.com/alitajs/umi…

Refer to the link

【Ant Design Runtime Theme Update 】: github.com/ant-design/…

【 ANTD-theme-generator 】 : github.com/mzohaibqc/a…

【 ANTD-theme -webpack-plugin】: github.com/mzohaibqc/a…