Ryan Shang

生死看淡,不服就干

0%

CSS切换主题方案

一、背景

在最近的一个需求中,一个商业产品要在不同APP中需要使用不同的CSS主题,这就涉及到了动态切换CSS主题的方案

二、方案简介

sass+mixin+js设置html的data-theme属性
原理是使用sass配合mixin编程成固定的css,通过js设定不同的主题,使对应的css生效

1. 定义变量

使用sass的map,可以把所涉及到的变量通过配置的方式整理起来,其中第一层是主题名,第二层是css的value,例如bg、info-price

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@use "sass:map";

$themes: (
appA: (
bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/2m9p9qlv__w1500_h520.png') no-repeat 0 0px / 100%,
info-price: #FF552E,
),
appB: (
bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/muc3req9__w1500_h520.png') no-repeat 0 0px / 100%,
info-price: #09D57E,
),
appC: (
bg: url('https://wos.58cdn.com.cn/cDazYxWcDHJ/picasso/7re7pg8r__w1500_h520.png') no-repeat 0 0px / 100%,
info-price: #FF704F,
),
);

2. mixin方法的实现

通过mixin实现把css根据不同主题注入页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@use "sass:map";

// 遍历主题数据,把@content中的css注入页面中
@mixin injectCss {
@each $theme-name, $theme-map in $themes {
//!global 把局部变量强升为全局变量,让下方的setCss方法也可以使用$theme-map
$theme-map: $theme-map !global;
//判断html的data-theme的属性值 #{}是sass的插值表达式
//& sass嵌套里的父容器标识 @content是混合器插槽,像vue的slot
[data-theme="#{$theme-name}"] & {
@content;
}
}
}

// 注入css
@mixin setCss($css, $variable) {
@include injectCss {
#{$css}: map-get($theme-map, $variable);
}
}

3. 通过js设置主题

1
2
3
4
5
6
7
const app = utils.getApp();
const themeMap = {
appA: 'appA',
appB: 'appB',
appC: 'appC',
};
window.document.documentElement.setAttribute('data-theme', themeMap[app] || 'appA');

4. 在页面中使用mixin

1
2
3
4
5
@import '@css/m/mini-theme.scss';

.buy-container {
@include setCss("background", "bg");
}