SoFunction
Updated on 2025-04-04

Vue+ElementUI realizes the entire process of dynamically changing any theme color (dynamic skinning)

Preface

Recently basedElementUIThe dynamic skinning function is required on the project, and here are two methods:

  • vue-element-adminOfficial implementation method
  • webpack-theme-color-replacerHow to implement plug-in

vue-element-admin official implementation method

Let me briefly explain its principle: After element-ui 2.0, all styles are written based on SCSS, and all colors are set based on several basic color variables, so it is not difficult to achieve dynamic skinning. Just find those color variables and modify it. First of all, we need to get the version number of element-ui through it, and request the corresponding style based on this version number. After getting the style, replace the color variable with what you need through regular matching and replacement, and then dynamically add the style tag to overwrite the original css style.

1. There is no brain reference herevue-element-adminSource code, nowstylesCreate a folder namedFile, and inThis file is introduced in .

/* src/styles/ */

/* Change the theme color variable */
$--color-primary: #256DCB;
$--color-success: #7AC800;
$--color-danger: #EC4242;
$--color-info: #999999;

/* Change icon font path variable, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';

@import "~element-ui/packages/theme-chalk/src/index";

/* If the exported variable value is undefined, the file name needs to be changed to */
:export {
  theme: $--color-primary
}
/*  */

import Vue from 'vue'
import Element from 'element-ui'
import './'
 
(Element)

2. It can be achieved through the above methodsElementUIThe theme has been replaced, but it is not enough to customize the color. Considering that the theme color is applied throughout the project, we save it toVuexThe next thing to do isstore/modulesNewly built

/* src/store/modules/ */

import variables from '@/styles/'
import * as Types from '../mutation-types'

const state = {
  theme: 
}

const mutations = {
  CHANGE_SETTING: (state, { key, value }) => {
    ('theme', value) // Cache and reuse it when refreshing    // eslint-disable-next-line no-prototype-builtins
    if ((key)) {
      state[key] = value
    }
  }
}

const actions = {
  changeSetting ({ commit }, data) {
    commit('CHANGE_SETTING', data)
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

3. This step is also brainless copyvue-element-adminThe source code ofThemePickerThe code of the component, as follows:

/* src/components/ThemePicker/ */

<template>
  <el-color-picker
    v-model="theme"
    :predefine="['#256DCB', '#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', '#F80', ]"
    class="theme-picker"
    popper-class="theme-picker-dropdown"
  />
</template>

<script>

import { chalkCss } from './' // It is the css obtained from the getCSSString interface. Here I fixed the version number and cached the css locally. Remember to deal with the \ escape problem
// const version = require('element-ui/').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // primary color in default color, calculate a series of colors based on this color for regular replacement
export default {
  name: 'ThemePicker',
  data () {
    return {
      chalk: '', // content of theme-chalk css
      theme: ''
    }
  },
  computed: {
    defaultTheme () {
      return this.$
    }
  },
  watch: {
    defaultTheme: {
      handler: function (val, oldVal) {
         = val
      },
      immediate: true
    },
    async theme (val) {
      const oldVal =  ?  : ORIGINAL_THEME
      if (typeof val !== 'string') return
      const themeCluster = (('#', ''))
      const originalCluster = (('#', ''))
      (themeCluster, originalCluster)

      const $message = this.$message({
        message: this.$t('theme_compiling'),
        customClass: 'theme-message',
        type: 'success',
        duration: 0,
        iconClass: 'el-icon-loading'
      })

      const getHandler = (variable, id) => {
        return () => {
          const originalCluster = (ORIGINAL_THEME.replace('#', ''))
          const newStyle = (this[variable], originalCluster, themeCluster)

          let styleTag = (id)
          if (!styleTag) {
            styleTag = ('style')
            ('id', id)
            (styleTag)
          }
           = newStyle
        }
      }

      if (!) {
        // const url = `/element-ui@${version}/lib/theme-chalk/`
        // await (url, 'chalk')
         = (/@font-face{[^}]+}/, '') // Local cache, if you need to get online, use the above method. Advantages: no delay in switching, disadvantages: manual maintenance of css string      }

      const chalkHandler = getHandler('chalk', 'chalk-style')

      chalkHandler()

      const styles = [].(('style'))
        .filter(style => {
          const text = 
          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
        })
      (style => {
        const { innerText } = style
        if (typeof innerText !== 'string') return
         = (innerText, originalCluster, themeCluster)
      })

      this.$emit('change', val)

      $()
    }
  },

  methods: {
    updateStyle (style, oldCluster, newCluster) {
      let newStyle = style
      ((color, index) => {
        newStyle = (new RegExp(color, 'ig'), newCluster[index])
      })
      return newStyle
    },

    getCSSString (url, variable) {
      return new Promise(resolve => {
        const xhr = new XMLHttpRequest()
         = () => {
          if ( === 4 &&  === 200) {
            this[variable] = (/@font-face{[^}]+}/, '')
            resolve()
          }
        }
        ('GET', url)
        ()
      })
    },

    getThemeCluster (theme) {
      const tintColor = (color, tint) => {
        let red = parseInt((0, 2), 16)
        let green = parseInt((2, 4), 16)
        let blue = parseInt((4, 6), 16)

        if (tint === 0) { // when primary color is in its rgb space
          return [red, green, blue].join(',')
        } else {
          red += (tint * (255 - red))
          green += (tint * (255 - green))
          blue += (tint * (255 - blue))

          red = (16)
          green = (16)
          blue = (16)

          return `#${red}${green}${blue}`
        }
      }

      const shadeColor = (color, shade) => {
        let red = parseInt((0, 2), 16)
        let green = parseInt((2, 4), 16)
        let blue = parseInt((4, 6), 16)

        red = ((1 - shade) * red)
        green = ((1 - shade) * green)
        blue = ((1 - shade) * blue)

        red = (16)
        green = (16)
        blue = (16)

        return `#${red}${green}${blue}`
      }

      const clusters = [theme]
      for (let i = 0; i <= 9; i++) {
        (tintColor(theme, Number((i / 10).toFixed(2))))
      }
      (shadeColor(theme, 0.1))
      return clusters
    }
  }
}
</script>

<style>
.theme-message,
.theme-picker-dropdown {
  z-index: 99999 !important;
}

.theme-picker .el-color-picker__trigger {
  height: 26px !important;
  width: 26px !important;
  padding: 2px;
}

.theme-picker-dropdown .el-color-dropdown__link-btn {
  display: none;
}
</style>

4. Finally use it in the pageThemePickerI'm putting the components hereheaderinside.

// template
<theme-picker @change="themeChange" />
// js
import ThemePicker from '@/components/ThemePicker'
// methods
themeChange (val) {
  this.$('settings/changeSetting', {
    key: 'theme',
    value: val
  })
}

So far, all dynamic skinning functions have been implemented, but there is another problem that the theme color will be reset after refreshing the page, so you need to cache the theme color tolocalStoreIn order to refresh the theme color on any page, I choseAdded a pageThemePickerFor components, see the following code for specific implementation:

/* src/ */

<template>
  <div  :style="{'--color': defaultTheme}">
    <theme-picker @change="themeChange" v-show="false" />
    <router-view />
  </div>
</template>

<script>
import ThemePicker from '@/components/ThemePicker'

export default {
  name: 'App',
  components: { ThemePicker },
  computed: {
    defaultTheme () {
      return this.$
    }
  },
  mounted () {
    if (('theme')) {
      (('theme'))
    }
  },
  methods: {
    themeChange (val) {
      this.$('settings/changeSetting', {
        key: 'theme',
        value: val
      })
    }
  }
}
</script>

Other styles defined by yourself can use them if you need to use the theme colorcssvariable, but need to be pre-orderedrootDefine the variables here, I use itvueBidirectional binding binds theme colors to:style="{'--color': defaultTheme}"(See the code above for the specific usage), so that the theme color can be used under any component (Note: This methodIEThe browser does not support it), the code is as follows:

<div class="box"></div>
.box {
  width: 100px;
  height: 100px;
  background-color: var(--color);
}

hint:

  1. If the scss file exports an empty object, you need to change the scss file name to the form.CSS Modules
  2. If you need to cache chalkCss, the \ in the css string will be escaped. Remember to manually change it to \\, otherwise the icons that come with ElementUI cannot be displayed.

Reference documentation:

  • vue-element-admin Dynamic skinning official document
  • ThemePicker component
  • CSS variables

How to implement webpack-theme-color-replacer plug-in

This method is available for personal testing, which is essentially a color replacement, but it is also a color replacement, butElementUIA little compatibility issue, see the specific implementation methodUse of webpack-theme-color-replacer, I won't go into too much detail here.

Summarize

This is the article about Vue+ElementUI dynamically changing any theme color (dynamic skinning) that ends with this article. For more related content related to Vue ElementUI dynamically changing any theme color, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!