Skip to main content

使用 SASS 实现主题切换

实现主题切换有两种主流的解决办法,使用 CSS 变量或者使用预编译器。考虑浏览器兼容性,所以选预编译器方式实现。

主题切换的原理很简单,在 html 标签上添加 data-theme 属性,根据不同的值匹配不同的样式。

例如我这里以 SASS 实现 data-theme="light"data-theme="dark" 两种主题为例,需要先掌握 SASS 的 @mixin、@each、@content、@function、map-get、@include 等用法。

定义不同主题下的颜色变量
$themes: (
light: (
bgColor: #fff,
textColor: #000
),
dark: (
bgColor: #000,
textColor: #fff
)
);
使用 mixin 自动生成不同主题
@mixin useTheme() {
@each $key, $value in $themes {
$curTheme: $key !global;
html[data-theme='#{$key}'] & {
@content;
}
}
}
  1. $curTheme: light 定义当前主题,默认是 light
  2. 使用 @each 遍历 $themes 变量,自动生成不同主题
  3. $key 代表 light 或者 dark,$value 代表各自后面的对象
  4. $curTheme: $key !global; 代表在每次循环种,将 $key 赋值给全局变量 $curTheme,全局变量在后面会用到
  5. & 代表使用 @include useTheme 的选择器
  6. @content 代表 @include useTheme() 括号内传递的内容
使用方式
.item {
width: 100px;
height: 100px;
@include useTheme {
background: #fff;
color: #000;
}
}

至此,上面的写法会生成如下的 css 代码:

.item {
width: 100px;
height: 100px;
}
html[data-theme='light'] .item {
background: #fff;
color: #000;
}
html[data-theme='dark'] .item {
background: #fff;
color: #000;
}

可以注意到 light 和 dark 的样式是一样的,因此 item 里面的值不能写死,我们要找到一种机制能够方便的读到对应主题的相应变量。我们可以定义一个函数 getVar,传入 key 读取当前主题下的变量值:

使用方式
.item {
width: 100px;
height: 100px;
@include useTheme {
background: getVar('bgColor');
color: getVar('textColor');
}
}
函数 getVar 实现
@function getVar($key) {
// 先取对应主题的变量对象
$themeValue: map-get($themes, $curTheme); // 再取变量对象里面的值
@return map-get($themeValue, $key);
}
  1. map-get 就是取对象的属性
  2. 其实在 useTheme 里面可以直接 $curThemeValue: $value !global;,那么这里 getVar 可以简写成 @return map-get($curThemeValue, $key)

至此,上面的写法会生成如下的 css 代码:

.item {
width: 100px;
height: 100px;
}
html[data-theme='light'] .item {
background: #fff;
color: #000;
}
html[data-theme='dark'] .item {
background: #000;
color: #fff;
}

最后我们调整下目录结构,将变量单独提取一个文件,将方法提取另一个文件,我们只需要维护变量文件,编写时引入方法文件使用 useTheme 即可。