Front-end skin change solution – element+less touchless skin change (no page refresh required)

Front-end skinning solution – element + less touchless skinning (no page refresh required)

Foreword

Not long ago, when revamping a project that had been iterated for more than a year, a skin-changing function was added. Through my own exploration, I have summarized a set of more suitable transformation plans for your reference. If you have a better plan, please leave your comments in the comment area

Talk about existing solutions

When reviewing existing solutions, several currently used solutions were summarized:

1. Define multiple sets of styles

First define one or more sets of style variables, including light and dark themes. Use variables in scss or less and change the class or attributes of the root node through js to achieve style override.
The front-end skinning solution implemented in this way may make styles difficult to manage and search for styles complicated. Each set of skins needs to write a CSS file, resulting in redundant multiple CSS codes.

$dark-fill-1: #222;
$dark-color-text: #fff;
$dark-color-text-1: rgba(255,255,255,0.3);
$dark-color-text-2: $color-brand1;

[data-theme="dark"] {<!-- -->
  body {<!-- --> background: $dark-fill-1; }
  .item .name {<!-- --> color: $dark-color-text; }
  .item .desc {<!-- --> color: $dark-color-text-1; }
  .header .text {<!-- --> color: $dark-color-text-2; }
}
2. Use less to pass parameters

Through the parameter passing attribute of less or scss, you can also change the page style by changing the root node class or attribute. The advantage of the first point is that you do not need to write multiple css files. The disadvantage is that when there are too many styles, it is passed in through methods. , there are too many parameters, and it is costly to change a certain color, which can easily cause problems.

.theme(
    @mainPageBG: #f4f6ff,
    @fColor: #1b1e29,
    @vanBgColor: rgb(198, 183, 140),
    @vanColor: #fff
) {
    .home {
        background: @mainPageBG;
        color: @fColor;
    }
}

.themeWhite {
    .theme(#fff, #1b1e29, rgb(198, 183, 140), #fff);
}
.themeBlack {
    .theme(#090c14, #fff, rgb(198, 183, 140), #fff);
}
3. VueUse + css variable quick switching

Use the external library VueUse’s useDark to quickly switch between dark and light modes.

<script setup lang="ts">
import { useToggle } from '@vueuse/shared'
import { isDark } from '../../.vitepress/theme/composables/dark'

// const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>

<template>
  <button @click="toggleDark()">
    <i inline-block align-middle i="dark:carbon-moon carbon-sun" />

    <span class="ml-2">{<!-- -->{ isDark ? 'Dark' : 'Light' }}</span>
  </button>
</template>

Final solution

After getting to know the above solutions, combined with the project’s own architecture (less + element + echarts + map), we finally adopted the following usage plan.

less variable processing

1. Define css variables for different skin colors

.themeDark {
    --theme-color: #141414;
    --header-primary-color: #1a3750;
    --bg-content-default: #1a3750;
    --bg-theme-default: #13293b;
    --bg-left-color: #27415a;
}

.themeLight {
    --theme-color: #f4f4f4;
    --header-primary-color: #2670ff;
    --bg-content-default: #fff;
    --bg-theme-default: #f3f5f9;
    --bg-left-color: #eff3f4;
}

2. Introduce css variables and use them by defining less variables to solve the problem of too many parameters when passing parameters through methods.

//Skin change related
@import './dark.less';
@import './light.less';

@theme-color: var(--theme-color-te);
@header-primary-color: var(--header-primary-color);
@bg-content-default: var(--bg-content-default);
@bg-left-color: var(--bg-left-color);
@bg-theme-default: var(--bg-theme-default);
@font-default-color: var(--font-default-color);
element theme switching

1. You can customize and download style files of different themes on the element official website, rename the css file to less, and add custom classes to the outermost layer.

.themeDark{
    //Downloaded css file
    ...
}

2. In order to avoid compiling this larger less file every time during development, use the less command to convert less to css.

lessc + space + less file name

final effect:

@charset "UTF-8";
.themeDark .fade-in-linear-enter-active,
.themeDark .fade-in-linear-leave-active {
  -webkit-transition: opacity 0.2s linear;
  transition: opacity 0.2s linear;
}
...

3. Introduced in main.js

echarts

Because a large number of echarts are used in the project, the echarts need to be reloaded to change when the skin color is dynamically switched. Therefore, it is necessary to listen to global variables and re-render the echarts when the skin is changed. Store the current theme in vuex

watch:{
        '$store.state.myTheme'() {
            this.getAllData();
        }
}
map

Tencent Maps are used in the project, so it is also necessary to change different map themes under different themes. When using a map, we know that when introducing a map, we need to import the map’s entry file. The entry file contains the map resources that need to be loaded, so we only need to define different theme colors on the Tencent Map official website and retain their style.json Next, modify the configuration in the entry file.

...
styleSrc: theme == 'themeDark' ? '/static/style.json' : 'https://xxxx/static/cdn/style{id}/style.json',
style3DSrc: theme == 'themeDark' ? '/static/style.json' : 'https://xxxx/static/cdn/style{id}/style.json',
...

js change theme

After completing the above preparations, change the attributes of HTML and vuex in main.js to change different themes without refreshing.

Store the theme that needs to be set in Storage to ensure that the theme color remains unchanged after refreshing. In order to be compatible with the UI framework (for example, the dialog will render the dom outside the body), so the class is placed on the top-level HTML and changes when the theme is changed. class on html, so that all less variables in the lower layer will use the color theme under this class. Modify the value of vuex to trigger the refresh of echarts.

/**
* Switch skin color
*/
changeColor(type) {
    this.themeClass = type;
    window.sessionStorage.setItem('themeClass', this.themeClass);
    //Dynamically modify the html class. In order to be compatible with the UI framework, put the class at the top level.
    const themeArr = ['themeDark', 'themeLight'];
    let tempArr = document.querySelector('html').classList;
    tempArr.forEach((item) => {
        if (themeArr.includes(item)) {
            document.querySelector('html').classList.remove(item);
        }
    });
    document.querySelector('html').classList.add(this.themeClass);

    //Modify the color of echarts
    if (type == 'themeLight') {
        Vue.prototype.$themeChartColor = '#333';
    } else {
        Vue.prototype.$themeChartColor = '#fff';
    }
    //Modify the value of the store, which can be used to trigger phase operations
    this.$store.commit('setMyTheme', type);
},

Conclusion

The above is the sharing of this skin change. Since the project did not take into account the subsequent need for skin change when it was first developed, we also encountered many detailed problems during this development, such as the theme color and various status color standards during previous development. The use is inconsistent. During the renovation, I tried to unify some similar colors to maintain the overall consistency of the system. This iteration also took a shorter time to implement this skin-changing function. In terms of overall efficiency, this solution and experience are relatively good.