网站主题

随着 CSS Variables 的普及,切换网站主题变成了一件轻松又愉快的工作,只需简单在 @media (prefers-color-scheme: dark) 下指定新的 --* 变量即可。但如果有多个主题,或者是主题嵌套该怎么办呢?
本文将告诉你如何解决这一切。

主题
纯色主题
带图主题
自定义主题
亮色主题
深色主题
主题管理器
document.body

主题复用

假设我们有三个主题,

  • 纯色默认主题 A (即 :root)
  • 暗色主题 B 继承自 A
  • 带图主题 C 继承自 A

很明显,我们可以写成下面这样的样式表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:root,
[data-theme='B'],
[data-theme='C'] {
--red: #f00;
--bg: #fff;
}

[data-theme='B'] {
--bg: #000;
}

[data-theme='C'] {
--background: url(c.png)
}

:root {
--bg: #fff;
}

但我们很快发现一个问题,主题间可能存在覆盖问题,A 与 B 的 CSS 优先级是一样的,如果有一个在下方就会被覆盖,那如何能加深这个深色模式呢?答案是加一层选择器。

我们发现 data-theme 已经不适合表达包含明暗和其他配色的多个主题了,于是用 data-mode 来表示明暗。

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
:root,
[data-theme='A'],
[data-theme='A'][data-mode='light'],
[data-theme='B'][data-mode='dark'],
[data-mode='dark'],
[data-theme='C'],
[data-theme='C'][data-mode='light'] {
--red: #f00;
--bg: #fff;
}

[data-mode='dark'],
[data-theme='B'][data-mode='dark'], {
--bg: #000;
}

[data-theme='C'],
[data-theme='C'][data-mode='light'] {
--background: url(c.png)
}

// 其他地方引入的样式

:root {
--bg: #fff;
}

看起来多了很多无用的代码,但我们列个表格来分析下各种组合。

明暗 A B C
Light 白底无图 N/A 白底带图
Dark N/A 黑底无图 N/A

可以看到最后一个 :root 由于优先级不如前方主题将不会覆盖前面的样式。

主题嵌套

前面我们解决了平级主题的复用,但如果是父子元素的嵌套呢?这时就变成了一个 CSS Variables 的嵌套。