css主题切换

前端主题切换方案

提前加载所有主题样式,切换时修改类名

实现

思路:提前将所有样式文件引入,然后在切换主题是给一个公共的元素添加一个类名,通过样式的层级来覆盖原有颜色相关样式

main.js中提前引入样式文件:

import '@/assets/css/dark.css'
import '@/assets/css/green.css'

green.css绿色主题css文件:

body.green .content {
  color: #333;
  background-color: #c7edcc;
}

原来的样式:

.content {
  color: #333;
  background-color: #fff;
}

切换主题时给公共的元素添加相对应类型来覆盖原有颜色样式:

    <button @click="changeStyle('dark')">切换成黑色主题</button>
    <button @click="changeStyle('green')">切换成绿色主题</button>
    changeStyle(color) {
      document.body.className = color
    }
优缺点

1》优点

不用重新加载样式文件,在样式切换时不会有卡顿

2》缺点:

首次就加载了所有的主题样式文件,牺牲了一点首屏时间来加载样式

一定要注意优先级的问题,主题切换的样式优先级要高于原有的才能覆盖

不是很灵活,没添加一个主题就要添加一个主题样式文件

link标签动态引入

实现

思路:也和前面一样先写好几套主题样式,然后切换时修改link标签的href属性

document.getElementById('#theme').href = 'green.css'
优缺点

1》优点

实现了按需加载,首屏更好

2》缺点

动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅

一定要注意优先级的问题,主题切换的样式优先级要高于原有的才能覆盖

不是很灵活,没添加一个主题就要添加一个主题样式文件

CSS变量+类名切换

和第一种方案类似,只不过不用写那么多样式,相当于方案1的优化版

CSS定义变量

–两个短横线用于定义CSS变量

    <div class="origin">
      祖先元素
      <div class="father">
        父亲
        <div class="child">
          儿子
          <div class="grandson">儿子的儿子</div>
        </div>
      </div>
    </div>

.origin {
  background-color: var(--theme-bg);
  color: var(--theme-font);
}
.father {
  --theme-bg: yellowgreen;
  --theme-font: red;
}
.child {
  background-color: var(--theme-bg);
  color: var(--theme-font);
}
.grandson {
  background-color: var(--theme-bg);
  color: var(--theme-font);
}

父元素(祖先元素)定义了css变量,子元素(后代元素)就可以通过var()来使用了

var()

语法:

var(<custom-property-name> ,<declaration-value>?)

var()是一个css函数,可以插入一个自定义属性,用来代替非自定义属性中 值的任何部分

第一个参数

作用:要替换的自定义属性的名称

第二个可选参数

可选参数

作用:用作回退值。如果第一个参数引用的自定义属性无效,则该函数将使用第二个值

:root

是一个伪类,用于匹配文档的根元素

1》可以直接在里面设置样式,就相当于给html设置样式

​      :root {
​        background-color: #ccc;
​        padding: 0 20px;
​      }

2》声明全局css变量,要以 “–” 开头,因为:root是根元素,根元素中定义变量就是全局变量啦

​      :root {
​        --pink-bg-color: pink;
​        --green-color: green;
​      }

通过var()函数就可以使用全局css变量了

      .box {
        background-color: var(--main-bg-color);
        color: var(--green-color);
      }
color-scheme

配色方案,normal就是浏览器默认配色方案,light白天模式,dark夜间模式,写了两个就是有个按顺序的优先级

color-scheme: normal;
color-scheme: light;
color-scheme: dark;
color-scheme: light dark;

color-scheme 的作用范围很有限,包括表单控件、滚动条和 CSS 系统颜色(浏览器内置的颜色)的使用值

可以搭配css变量等进行主题切换

实现

思路:依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量对应的取值即可

具体实现:

引入主题样式文件:theme.css

:root {
  --bg-color: #eee;
  --font-color: #333;
}
.red {
  --bg-color: #a61840;
  --font-color: #fbddc2;
}
.green {
  --bg-color: #efa5a9;
  --font-color: #ede1bc;
}
.blue {
  --bg-color: #4e3bb1;
  --font-color: #dfb9c8;
}

通过var()给盒子设置颜色

.colorful-wrapper {
  background-color: var(--bg-color);
  color: var(--font-color);
  border-color: var(--border-color);
}
html.red {
  color-scheme: red;
}
html.green {
  color-scheme: green;
}
html.blue {
  color-scheme: blue;
}

切换主题时:给根元素添加对应的类名

      document.documentElement.className = color
优缺点

优点:

不用重新加载样式文件,在样式切换时不会有卡顿

在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题

新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换

缺点:

首屏加载时会牺牲一些时间加载样式资源,除了IE的兼容优点问题基本上可以不用考虑兼容问题

CSS变量+动态setProperty

setProperty

语法:

style.setProperty(propertyName, value, priority)

propertyName:属性名

value:属性值,可选参数;如果没有指定,则当作空字符串;不能直接在value后加 !import, 设置important需要使用priority来设置

priority:优先级,可选参数,允许设置"important" 优先级

返回值为undefined

实现

通过:root定义全局css变量,给对应盒子设置颜色时通过var()函数将css变量值设置给对应属性

<style>
:root {
  --bg-color: #f5f5f5;
  --font-color: #333;
  --border-color: #ccc;
}
.colorful-box {
  background-color: var(--bg-color, #f5f5f5);
  color: var(--font-color, #333);
  border-color: var(--border-color, #ccc);
}
</style>

修改主题:通过setProperty来修改变量的值

export const changeTheme = function (theme, ele = document.documentElement) {
  for (let [key, value] of Object.entries(themes[theme])) {
    ele.style.setProperty(key, value)
  }
}

export const themes = {
  red: {
    '--bg-color': '#a61840',
    '--font-color': '#fbddc2',
  },
  green: {
    '--bg-color': '#efa5a9',
    '--font-color': '#ede1bc',
  },
  blue: {
    '--bg-color': '#4e3bb1',
    '--font-color': '#dfb9c8',
  },
}
优缺点

1》优点:

不用重新加载样式文件,在样式切换时不会有卡顿

新增或修改主题方便灵活

2》缺点:

IE的兼容其他基本浏览器基本不用太考虑

SASS的mixin混入+类名切换

theme.scss

// 白色主题
$bg-color-light: #fff;
$font-color-light: #333;
$border-color-light: #999;
$bg-image-light: url("src/images/light.png");

// 黑色主题
$bg-color-dark: #999;
$font-color-dark: #fff;
$border-color-dark: #000;
$bg-image-dark: url(src/images/dark.png");

mixin.scss

@import "./theme.scss";
@mixin bg_image($img) {
  background-image: $img;
  [data-theme="dark"] & {
    background-image: $bg-image-dark;
  }
  [data-theme="light"] & {
    background-image: $bg-image-light;
  }
}

@mixin font_color($color) {
  color: $color;
  [data-theme="dark"] & {
    color: $font-color-dark;
  }

  [data-theme="light"] & {
    color: $font-color-light;
  }
}

@mixin border_color($color) {
  border-color: $color;
  [data-theme="dark"] & {
    border-color: $border-color-dark;
  }
  [data-theme="light"] & {
    border-color: $border-color-light;
  }
}

@mixin bg_color($color) {
  background-color: $color;
  [data-theme="dark"] & {
    background-color: $bg-color-dark;
  }
  [data-theme="light"] & {
    background-color: $bg-color-light;
  }
}

vue文件中使用

<template>
  <div class="wrapper">
    <div>
      <button @click="setTheme('light')">白天模式</button>
      <button @click="setTheme('dark')">深夜模式</button>
    </div>
    <div class="content">文字text</div>
    <div class="img"></div>
  </div>
</template>
<script>
export default {
  methods: {
    setTheme(theme) {
      document.documentElement.setAttribute("data-theme", theme);
    },
  },
};
</script>
<style lang="scss" scoped>
@import "../assets/scss/mixin.scss";
.wrapper {
  @include bg_color($bg-color-dark);
  @include font_color($bg-color-dark);
}
.img {
  width: 800px;
  height: 400px;
  border: 1px solid red;
  @include bg_image($bg-image-dark);

}
</style>
优缺点:

优点:

灵活

兼容性好

缺点:

vue3才支持

在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能)

Vue3

基本使用

v-bind可以绑定data中的变量

原理其实就是给元素绑定CSS变量,在绑定的数据更新时调用 CSSStyleDeclaration.setProperty 更新CSS变量值

const theme = {
  color: "red",
};


.box {
  color: v-bind("theme.color");
}
优缺点:

优点:

灵活,易用,切花不需要重新加载样式文件

缺点:

vue3才支持

在组件上绑定了动态样式的地方都会有对应的编译成哈希化的CSS变量,而不像方案3统一地就在:root上设置(不确定在达到一定量级以后的性能)

如果主题样式不确定,用户可自定义,使用 CSS变量+动态setProperty 最合适

如果主题固定使用选择更广,大部分都是选择 CSS变量+类名切换 这种方案