前言

现在换肤的场景越来越多,特此总结一下自己使用过切实可行的两种换肤方案,less和sass各一种,less配合的vue项目,sass配合的react项目;大体的思路都是一样的:

1.将主题会用到的颜色都使用变量定义;
2.使用less/sassmixin和函数实现不同主题的颜色选取;
3.在body上放一个用于区别主题的类名或者属性,通过动态更换这个类名,实现主题的切换;

Less

1.效果

定义了两套主题:蓝色和橙色
先看效果吧,把类名改为theme_blue切换为theme_orange主题直接就换掉啦~
蓝色主题
橙色主题

2.定义变量

  1. 定义变量;
  2. 定义了一个叫theme的mixin,把定义的变量传入mixin内部;

theme.blue.less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//主题颜色 蓝
@theme: #1989fa;
@theme_to: #1989fa;
@theme_black: #272b2e; // 官网黑
// 渐变
@linear: #1989fa;
@linear_to: #1989fa;
//常用颜色
@Black: #000000; // 白底的须知
@White: #ffffff; // 白色底或文字
@GeneralGray: #999999; // 正常灰色字体,多用于值的显示
@DeepGray: #333333; // 深灰色字体,多用于表单页面的参数名和标题
@AuxiliaryGray: #dcdcdc; // 常用于分割线
@WhiteGray: #f8f8f8; // 页面最底层灰色背景
@borderGray: #ebedf0; // border
@Divider: #dcdcdc;
@backgroundAlert: #f1f3f4; // alert背景色
@buttonShadowColor: #1989fa; // button shadow
@LightGrey: rgba(77, 85, 93, 0.2); // 淡灰色, 一般用于按钮等
@border: 1px solid #ebedf0;


// theme mixin把变量传入
.theme(
@theme,
@theme_to,
@Black,
@White,
@GeneralGray,
@DeepGray,
@AuxiliaryGray,
@WhiteGray,
@backgroundAlert,
@borderGray,
@Divider,
@buttonShadowColor,
@LightGrey,
@linear,
@linear_to,
@theme_black
);


theme.orange.less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

//主题颜色 橙
@theme: #f87f24; // 橙色选中高亮 #ee4a02
@theme_to: #ff8917; // 选中高亮 #ff8917
@theme_black: #272b2e; // 官网黑
// 渐变
@linear: #ff8700;
@linear_to: #ffbd37;

//常用颜色
@Black: #000000; // 白底的须知
@White: #ffffff; // 白色底或文字
@GeneralGray: #999999; // 正常灰色字体,多用于值的显示
@DeepGray: #333333; // 深灰色字体,多用于表单页面的参数名和标题
@AuxiliaryGray: #dcdcdc; // 常用于分割线
@WhiteGray: #f5f5f5; // 页面最底层灰色背景
@borderGray: #ebedf0; // border
@Divider: #dcdcdc;
@backgroundAlert: rgba(255, 108, 108, 0.2);
@buttonShadowColor: rgba(255, 134, 0, 0.51); // button shadow
@LightGrey: rgba(211, 211, 211, 0.4); // 淡灰色, 一般用于按钮等
@border: 1px solid #ebedf0;

// theme mixin把变量传入
.theme(
@theme,
@theme_to,
@Black,
@White,
@GeneralGray,
@DeepGray,
@AuxiliaryGray,
@WhiteGray,
@backgroundAlert,
@borderGray,
@Divider,
@buttonShadowColor,
@LightGrey,
@linear,
@linear_to,
@theme_black
);

3.在less中使用

index.less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 公共样式
@import '../common.less';
// 页面样式入口
@import '../main.less';

// theme_blue、theme_orange是用来切换主题的类名,类名不同应用不同的主题变量
.theme_blue {
// 引入定义的theme函数和变量
@import './theme.blue.less';
}

.theme_orange {
@import './theme.orange.less';
}

// 可扩展其他主题 ...

把有主题变动的元素写在mixin内部,颜色都要使用mixin传进来的变量,这样当主题类名更换了之后,mixin传入的变量值就随之更换,从而更换了主题;

main.less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 // mixin调用
.theme(
@theme,
@theme_to,
@Black,
@White,
@GeneralGray,
@DeepGray,
@AuxiliaryGray,
@WhiteGray,
@backgroundAlert,
@borderGray,
@Divider,
@buttonShadowColor,
@LightGrey,
@linear,
@linear_to,
@theme_black) {

.text_width_limit() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.bgc_Gray() {
height: 100%;
background-color: @WhiteGray;
}

//背景渐变颜色 Gradual change
.theme_linear() {
background: linear-gradient(90deg, @linear 25%, @linear_to 100%) !important;
}

.theme_shadow() {
box-shadow: 1px 1px 5px 0 @theme;
}

// 在这里引入具体的模块的样式文件
@import './a.less';
@import './b.less';
// more ...
}

sass

1.效果

这里使用了React16的context配合做的主题的切换;具体代码可以查看 上一篇博客;在这里我们只关注切换代码的css部分如何去实现;



2.定义变量

_variable.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 全局变量
$white: #FFFFFF;
$grayTheme:#262D33;
$gray:#b5b5b5;
$blue:#2173dc;
$deepBlue:#3f51b5;
$purple: #673ab7;
$mask:rgba(55, 55, 55, .5);
$defaultFontColor:#515a6e;


// 主题 ['theme-gray', "theme-blue", "theme-deepBlue", 'theme-purple']
// 主题定义
$themes:(
gray:(
theme: #262D33,
deep-theme: #131619,
bg-color: $white,
font-color: #b5b5b5,
deep-font-color: $defaultFontColor,
header-font: #515a6e,
header-bgc: $white,
menu-bgc: #262D33,
menu-hover: $white,
menu-hover-bgc: #414d58,
menu-active: $white,
menu-active-bgc: $blue
),
blue:(
theme: $blue,
deep-theme: $blue,
bg-color: $white,
font-color: $defaultFontColor,
deep-font-color: $defaultFontColor,
header-font: $white,
header-bgc: $blue,
menu-bgc: $white,
menu-hover: $blue,
menu-hover-bgc: #f0faff,
menu-active: $white,
menu-active-bgc: $blue,
),
deepBlue:(
theme: $deepBlue,
deep-theme: $deepBlue,
bg-color: $white,
font-color: $defaultFontColor,
deep-font-color: $defaultFontColor,
header-font: $white,
header-bgc: $deepBlue,
menu-bgc: $white,
menu-hover: $deepBlue,
menu-hover-bgc: #f0faff,
menu-active: $white,
menu-active-bgc: $deepBlue,
),
purple:(
theme: $purple,
deep-theme:$purple,
bg-color: $white,
font-color: $defaultFontColor,
deep-font-color: $defaultFontColor,
header-font: $white,
header-bgc: $purple,
menu-bgc: $white,
menu-hover: $purple,
menu-hover-bgc: #eee6fb,
menu-active: $white,
menu-active-bgc: $purple,
)
)

3.定义mixin

_theme.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 引入变量
@import './_variable.scss';
// 主题 ['theme-gray', "theme-blue", "theme-deepBlue", 'theme-purple']

// 遍历主题map 取当前主题下的变量;$theme-map就是对应主题的配置
@mixin themeify {
@each $theme-name, $theme-map in $themes {
// !global 把局部变量强升为全局变量
$theme-map: $theme-map !global;
// 判断html的data-theme的属性值 #{}是sass的插值表达式
// & sass嵌套里的父容器标识 @content是混合器插槽,像vue的slot
[data-theme="theme-#{$theme-name}"] & {
@content;
}
}
}

// 声明一个根据Key获取颜色的function
@function themed($key) {
// map-get 根据对应的key值返回map中对应的值
// map-get($map, $key)
@return map-get($theme-map, $key);
}

// 主题背景颜色
@mixin background_color($color) {
@include themeify {
background-color: themed($color) !important;
}
}

// 获取字体颜色
@mixin font_color($color) {
@include themeify {
color: themed($color) !important;
}
}

// 获取边框颜色
@mixin border_color($color) {
@include themeify {
border-color: themed($color) !important;
}
}

// 改svg颜色
@mixin svg_color($color) {
@include themeify {
fill: themed($color) !important;
}
}

4.在sass中使用mixin

layout.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// 引入mixin
@import './theme/_theme.scss';


/* 1 header & header elements */
header {
svg{
@include svg_color("header-font");
}
@include font_color("header-font");
@include background_color("header-bgc");
box-shadow: 0 0 2px rgba(0, 0, 0, .3);
z-index: 1;
}



/* 2 menu */
.sidebar {
width: 220px;
@include background_color("menu-bgc");
position: relative;
transition: width .3s ease;

.logo {
height: 55px;
border-bottom: 1px solid #f5f4f4;
}

.nav-item {
cursor: pointer;
box-sizing: border-box;
position: relative;
transition: height .5s ease;

p {
@include font_color('font-color');
height: 40px;
font-weight: 400;
font-size: 13px;
text-decoration: none;

i {
font-size: 14px;
margin-right: 12px;
@include font_color('font-color');
}
}

}
}

/* 2.2 menu normal */
.sidebar_normal {
.nav-item {
overflow: hidden;

p {
&:hover {
@include font_color('menu-hover');
@include background_color('menu-bg-hover');
border-right: 2px solid;
@include border_color('theme');
}

&.active {
i {
@include font_color('menu-active');
}

@include font_color('menu-active');
@include background_color('menu-active-bgc');
}
}
}
}

/* 2.2 menu mini */
.sidebar-mini {
width: 70px;
transition: width .3s ease;

.child-item {
display: none;
}

.nav-item {

p {
@include font_color('deep-font-color');
i {
@include font_color('menu-hover');
}
&:hover {
@include font_color('menu-hover');
@include background_color('menu-bg-hover');
}

&.active {
i {
@include font_color('menu-active');
}

@include font_color('menu-active');
@include background_color('menu-active-bgc');
}
}

&:hover {
.child-item {
display: block;
position: absolute;
width: 220px;

@include background_color('bg-color');
left: 68px;
top: 0px;
border-radius: 0 3px 3px 0;
}
}

}

.fa-angle-down {
transition: all .2s ease;
transform: rotate(-90deg);
}

}

// ...