Antd Dynamic Theme

The demo warehouse

The original post links

Plan the point

  1. Use the ANTD-theme-generator plug-in
  2. To give upcss-module(this plug-in is not supported), use it insteadCSS-BEM

steps

1. Install dependencies

Yarn add @craco/craco craco-antd [email protected] -d yarn add less antd@^3Copy the code

Since the project uses antd@3, you need to install the specified version [email protected]. If you install the latest version, an error will be reported.

If antd@4 is used, install the antD-theme-Generator of the latest version.

2. Create a variable definition file

src/styles/variables.less

This file contains all antD defined variables (functions) as well as custom variables within the project

// We need to introduce the antd variable first
@import '~antd/lib/style/themes/default.less';

// Configure the antD topic variable with default values
@primary-color: rgb(42.187.103);

// Project internal variables (do not conflict with antD variables)
@theme-color: rgb(42.187.103);
Copy the code

3. Create a script file

scripts/color.js

This file is used to generate less style files that can be run in production mode

// AntD theme color file generation script

const fs = require('fs')
const path = require('path')
const { generateTheme } = require('antd-theme-generator')

const themeVariables = ['@primary-color'.'@theme-color']

// Because the [email protected] library is different, version 1.2.6 is required
const options = {
  // AntD library path
  antDir: path.join(__dirname, '.. /node_modules/antd'),

  // The root of all less files to retrieve
  stylesDir: path.join(__dirname, '.. /src'),

  // Custom variable files
  varFile: path.join(__dirname, '.. /src/styles/variables.less'),

  // Which variable values need to be dynamically modified
  themeVariables,
}

// Since much of the style content extracted by the plug-in is redundant, the plug-in's default export function is not used
// You need to process the data first and then export the file yourself
generateTheme(options)
  .then((less) = > {
    console.log("Less file contents extracted successfully");

    // The location of the generated less
    const outputFilePath = path.join(__dirname, ".. /public/color.less");

    let content = less.replace(/([,{])\n/g."$1");

    const arr = content
      .split("\n")
      .map((str) = > str.trim())
      .filter((str) = > {
        // Pure class style or variable definition
        const isClassStyleOrVars =
          / ^ \. (? ! (\ \)). * \ {*? \}$/gm.test(str) || / ^ @. *? :. *? ; $/gm.test(str);

        // The string does not contain arbitrary subject variables
        const excludeThemeVars = themeVariables
          .map((k) = > k.slice(1))
          .every((k) = >! str.includes(k));// There are other redundancies, but they don't matter much
        return! (isClassStyleOrVars && excludeThemeVars); }); content = arr.join("\n");
    fs.writeFileSync(outputFilePath, content);

    console.log("Theme style file compiled successfully");
  })
  .catch((error) = > {
    console.log("Error", error);
  });
Copy the code

4. Pack the configuration

Purpose: To have the framework support less; And introduce antD style files (less)

Since the project was generated based on create-React -app and did not want to use YARN eject, craco was used for custom configuration.

Of course, other methods can be used, as long as the ANTD components work properly.

New craco. Config. Js

const CracoAntDesignPlugin = require("craco-antd");

module.exports = {
  plugins: [
    // AntD is loaded on demand (instead of using this plugin, global import ANTD style is also ok)
    {
      plugin: CracoAntDesignPlugin,
      options: {
        babelPluginImportOptions: {
          libraryName: "antd".libraryDirectory: "es".style: true,},},},],};Copy the code

Modify the package. The json

{
  "scripts": {
    "dev": "node ./scripts/color.js && craco start"."build": "node ./scripts/color.js && craco build"."color": "node ./scripts/color.js",}}Copy the code

Run the project first to check whether the configuration is successful

yarn dev
Copy the code

5. Modify the index. HTML entry file

public/index.html

<! DOCTYPE html> <html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json"/> </head> <body> <! -- antd theme less --> <link rel="stylesheet" type="text/less" href="%PUBLIC_URL%/color.less" />
    <script>
       // https://less.bootcss.com/usage/#browser-usage-setting-options
       window.less = { javascriptEnabled: true, logLevel: 3 } </script> <! -- antd theme less end --> <noscript>You need toenable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
Copy the code

Pay attention to

The imported less tag needs to be written under the body tag, otherwise it will be overwritten by the dynamically imported style file (CSS weights issue)

6. Import less in the project entry file

scr/index.jsx

import React from 'react';
import ReactDOM from 'react-dom';

// Introduce less to initialize the topic configuration
import 'less'

import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>.document.getElementById('root'));Copy the code

7. Run the command to view the result

yarn color
yarn dev
Copy the code

After yarn Color is run, a public/color.less file is generated, and a CSS style label is automatically generated based on this less file during initialization of the running project

8. Change theme colors dynamically

Use less.modifyVars() to modify the less variable. A component is provided for your reference.

import React, { useCallback } from 'react'
import { Button, DatePicker, Pagination } from 'antd'
import less from 'less'

const colors = ['#a12356'.'#0215a6'.'#f120a1']

const ThemeSetter = () = > {
  const onColorChange = useCallback((color) = > {
    less.modifyVars({
      '@primary-color': color,
      '@theme-color': color,
    })
  }, [])

  return (
    <>
      <h1>color setter</h1>
      {colors.map((c) => (
        <button key={c} onClick={()= > onColorChange(c)}>
          {c}
        </button>
      ))}

      <hr />
      <Button>A</Button>
      <Button type='primary'>A</Button>
      <DatePicker />
      <Pagination total={100} />
    </>)}export default ThemeSetter
Copy the code

conclusion

Let’s go through the principles. First, make sure your project compiles less style files and introduces ANTD as normal.

Core file scripts/color.js; public/color.less

  1. Based on configuration itemsvarFile: path.join(__dirname, '.. /src/styles/variables.less')Find all the less variables you need to track
  2. fromantDir: path.join(__dirname, '.. /node_modules/antd')The antD library file specified in the configuration item traverses all less files and extracts all containsVariables to be tracked(mainly ANTD) code
  3. fromstylesDir: path.join(__dirname, '.. /src')The path specified in the configuration item traverses all less files and extracts all containsVariables to be tracked(mostly custom) code
  4. generatepublic/color.lessfile
  5. public/index.htmlRefer to thepublic/color.less; And when the page is initialized, passlessTo compile the correspondingcssStyle.
  6. In the page, antD components or components that use theme variables are first rendered using compiled CSS styles from less files introduced in the component, and then rendered bypublic/color.lessThe compiled styles are overwritten and re-rendered. Achieve the effect of modifying the color style
  7. In the meantime, throughless.modifyVars()Compile the new style again, overwrite the style again and render. To achieve dynamic modification of the theme color function

Note that each time you modify or add a custom variable or style file, you need to execute YARN Color to regenerate the theme file. In a development environment, however, this is typically done only once before packaging.