First, background

  • In the new requirements, the visual designer’s request to change the original theme color of the project fromyellowReplace withblue
  • This action is not known before the item is packaged, but is switched after the package based on conditions. So you need to keep two sets of styles: yellow theme skin and blue theme skin
  • The Element-UI component library also needs to implement theme color peels

Second, the current stage of skin changing scheme

  • Due to the time, the skin changing scheme adopted at the present stage is to write two sets of theme color skin, using the simplest scheme with strong coverage
/** * blue theme style */
@themeColor: blue;
.au-bu-theme{// The imported style file has been extracted with style variables@import '.. /less/com.less'; .@import '.. /less/bottom-button.less';
}
Copy the code
  • Most notable problem: code is super hard to maintain!!

Iii. Exploration of simpler skin changing schemes

1. Scheme 1: Skin changing scheme of Ant-Design

  • ant-designWebsite address:www.antdv.com/docs/vue/cu…
  • Reasons to consider,ant-designThe component library style is adoptedlessIt is similar to the current project and has reference value
  • Code implementation:
// vue-cli2
// build/utils.js
- less: generateLoaders('less'),

+ less: generateLoaders('less', {
+   modifyVars: {
+     'primary-color': '#1DA57A',
+     'link-color': '#1DA57A',
+     'border-radius-base': '2px',
+   },
+   javascriptEnabled: true, +}),Copy the code
  • Problem encountered: Ant-Design uses a global style variable replacement skin approach before packaging that doesn’t meet our needs, so pass

2. Programme II:var()In combination with:rootPseudo element skin scheme

:root This CSS pseudo-class matches the root element of the document tree. For HTML, :root represents the element and is the same as an HTML selector, except that it has a higher priority. — MDN: link developer.mozilla.org/zh-CN/docs/…

  • This scheme is the simplest and easiest to understand

  • Implementation code:

/ / in directly.lessIn the file.:rootIt always becomes <html> the pseudo-element of the tag:root {
    '--primary-color': #1DA57A; } // compatible with the previous variable writing@themeColor: var(--primary-color);
Copy the code
// Change the variable --primary-color to achieve peels
let docEle = document.documentElement
const auTheme = {
    '--primary-color': 'bule';
    '--hello-color': 'red';
}
// Batch overwrite style
for (const key in auTheme) {
    if (auTheme.hasOwnProperty(key)) {
        const value = auTheme[key];
        / / key
        docEle.style.setProperty(key, value)
    }
}
// Release memory to avoid memory overflow
docEle = null
Copy the code
  • Problem: IE does not support itvar()

3. Scheme 3: Skin-changing implementation of Element-UI

  • Component library official website: element.eleme.cn/2.7/#/zh-CN…
  • The home page has the component library to switch the realization of the skin, if you can refer to the implementation of the scheme, the skin scheme will be much simpler, the skin effect is as follows GIF

  • Problem encountered: The official documentation only describes how to replace styles before packaging
  • Ele. me github responded

The project warehouse is here: github.com/ElementUI/t… The implementation is quite violent: first, replace the CSS values in the default theme file with keywords: github.com/ElementUI/t… Generate a series of color values based on the theme color selected by the user: github.com/ElementUI/t… Replace the keyword with the corresponding color value you just generated: github.com/ElementUI/t… Simply tag the page with the style tag and fill in the generated style: github.com/ElementUI/t…

  • Take the cssText of all the components and match the color value to be changed. After the color value is replaced, the cssText is overwritten and a tag is generated with innerText = cssText

Four, thinking

1. Incorporate your own projects

  • Ele. me component library theme color overlay, the scheme adopted is to introduce the style first and then the corresponding theme color value overlay
  • Do the first skin scheme, the global style has been theme color arrangement
  • There will be no active skin changing operation, skin changing operation will only be triggered in specific circumstances, belonging to low frequency behavior

2. Initial viable ideas

  • Considering the size and workload of the project, the method of overwriting component library theme colors is not replaced. While the first scheme does not support dynamic skin changing, the third scheme is heavy and tedious, and it also faces some side effects caused by strong style coverage. Finally decided to use plan three: adopt CSS3var()In combination with:rootProperty to implement the theme color.
  • The current advantage is that the global theme color style has been extracted. The implementation method of scheme three is adopted, and the change is minimal.

3. Problems to be solved:

  • The range ofvar()Grammar inieThe browser does not support this function

V. Solutions

  • Globally configuredlessStyle variables
  • All theme color variables need to be replaced (usingvscodeOne-key replacement capability)
  • In the scene that needs to skin to the new theme style in the notes, using the principle of CSS with the same name to cover, to achieve the purpose of skin

Six, concrete implementation

Premise: The project adopts VUE-CLI2 implementation, the following code is based on VUe-cli2

1. The configuration:root

// yellow.css
:root {
    --theme-color: yellow;
}
Copy the code
// blue.css
:root {
    --theme-color: blue;
}
Copy the code
// index.html
<html>
    <head>// Write directly to the HTML file to avoid color flickering after the page is loaded<link rel="stylesheet" type="text/css" href="yellow.css">
    </head>
</html>
Copy the code

2. Configure the global configurationlessvariable

  • vue-cli2
$yarn add sass-resources-loader -dCopy the code
// variables.less
@themeColor: var('--theme-color'); // get the global cSS3 variableCopy the code
// util.js
exports.cssLoaders = function (options) {
	function generateLoaders (loader, loaderOptions) {
    let loaders = [cssLoader]
        if (loader) {
            loaders.push({
                loader: loader + '-loader'.options: Object.assign({}, loaderOptions, {
                    sourceMap: options.sourceMap
                })
            })
        }

        /** * Add a sass-resources-loader to the less-loader to inject less variables */
        if (loader === "less") {
            loaders.push({
                // Not Only For Sass
                // https://github.com/shakacode/sass-resources-loader/issues/31
                loader: "sass-resources-loader".options: {
                    resources: path.resolve(__dirname, '.. /src/variables.less')}}}// Extract CSS when that option is specified
        // (which is the case during production build)
        if (options.extract) {
            return ExtractTextPlugin.extract({
                use: loaders,
                fallback: 'vue-style-loader'})}else {
            return ['vue-style-loader'].concat(loaders)
        }
    }
}

Copy the code
  • vue-cli3
$ yarn add style-resources-loader -D
$ yarn add vue-cli-plugin-style-resources-loader -D
Copy the code
// vue.config.js

pluginOptions: {
    'style-resources-loader': {
        'preProcessor': 'less'.// Configure global less variables and methods in variable.less. Support for multiple files
        'patterns': [
            // Change to your own address
            path.resolve(__dirname, './src/common/assets/css/variable.less')]}}Copy the code

3. Change skin

Css3 var gasket plug-in CSS-vars-ponyfill is found on NPM after searching online information. Garbage IE finally saved

$yarn add CSS-vars-ponyfill $yarn add mutationObserver-shimCopy the code
// changeTheme.js
import 'mutationobserver-shim' / / compatible with Internet explorer
import cssVars from 'css-vars-ponyfill' // CSS var spacer

// Dynamically generate link labels
function createLink (name) {
    let docu = documeent
    douc.createElement('link')
    link.setAttribute('rel'.'stylesheet')
    link.setAttribute('type'.'text/css')
    link.setAttribute('href'.'`./${name}.css`') // Own file address
    douc.head.appendChild(link)
    docu = null
}

// Check whether it is Internet Explorer
function isIe () {}// Load a new CSS variable file, overwriting the default CSS variable file, to achieve the purpose of the skin
function changeTheme () {
    createLink('blue')}export default function () {
    if (isIe()) {
        The job of cssVars is to put in a macro task after the DomContentload event to ensure that it can be executed
        setTimeout(function () {
            changeTheme()
            cssVars({
                watch: true})},0)}else {
        changTheme()
    }
}
Copy the code
// main.js implements the call
import changeTheme from 'changeTheme.js'

changeTheme()
Copy the code

Seven,

  • A globallessSort through variables, get through all of them.vueand.lessFile. The globallessVariable referenceCSS 3 variablesWe just need to changeCSS 3 variablesUndertake modification, can achieve the purpose that changes skin
  • Advantages: after transformation, once and for all. Dark mode and many other skins can be implemented in this way
  • Cons: There is a slight flicker when changing skin, which can be optimized according to the scene
  • css-vars-ponyfillCompatible withieBrowser principle: usejswillvar(*)The variable is converted to a specific value and then passed<style>Tag inserted into<head>In terms of performance issues (ieWhat are you looking at me for? .

Shoulders of Giants (Resources)

www.cnblogs.com/leiting/p/1… www.sitepoint.com/css-theming… Github.com/ElemeFE/ele… Github.com/ElementUI/t… www.cnblogs.com/jofun/p/119… zhuanlan.zhihu.com/p/149033179

Finally, good study will not be bad! I love you guys. Give it a thumbs up