原理

先说一下效果:实现了两种模式(这里都是基于 Typecho 博客来测试的)

  1. 支持用户选择跟随系统模式自动变化的自动模式
  2. 支持用户手动切换深色模式与浅色模式

下面简单说一下原理,方便理解

1、标志

这里在 <body> 上设置了三种标志用于区分,data-theme="auto"data-theme="light"data-theme="dark"

  • auto (跟随系统) 下,主题显示根据 prefers-color-scheme 来显示,也就是系统当前的显示模式决定网站的模式,如系统现在处于深色模式,则网站也是深色模式

  • lightdark 模式下,则分别显示对应的 data-theme="light" 或者 data-theme="dark" ,不再跟随系统设置,而是用户手动选择的显示模式

2、逻辑

用户第一次访问网站,默认是 auto 模式,此时网站是切换为深色模式还是保持不变,由系统是否处于深色模式决定

当用户手动切换模式后,data-theme="light" 或者 data-theme="dark" ,此时网站主题不再跟随系统变化

CSS 部分

自己修改网站的 CSS 部分

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
/* 定义基础颜色,即不论是深色模式还是浅色模式都会用到的 */
:root {
--color-white: #fff;
--color-red: #c00;
--color-gray: #999999;
--color-yellow: #ffc93e;
--color-hightlight: #f47466;
}

/* 用户首次访问网站由系统当前的模式决定是深色模式还是浅色模式 */
body[data-theme='auto'] {
/* 浅色模式下对应的颜色 */
--color-background: #f5f5f5;
--color-fontcolor: #363636;
--color-url: #3354aa;
--color-url-hover: #444;
--color-code: #f3f3f3;
}
@media (prefers-color-scheme: dark) {
/* 如果系统为深色模式,深色模式下对应的样式 */
body[data-theme='auto'] {
--color-background: #2c2a2a;
--color-fontcolor: #d5d5d5;
--color-url: #f0f0f0;
--color-url-hover: #8ab4f8;
--color-code: #b0b0b0;
}
}

/* 用户手动设定了网站颜色为浅色的话 */
body[data-theme='light'] {
--color-background: #f5f5f5;
--color-fontcolor: #363636;
--color-url: #3354aa;
--color-url-hover: #444;
--color-code: #f3f3f3;
}

/* 用户手动设定了网站颜色为深色的话 */
body[data-theme='dark'] {
--color-background: #2c2a2a;
--color-fontcolor: #d5d5d5;
--color-url: #f0f0f0;
--color-url-hover: #8ab4f8;
--color-code: #b0b0b0;
}

/* 网站的样式使用颜色,比如 */
body {
background-color: var(--color-background);
color: var(--color-fontcolor);
}

/* 就是将网站样式涉及到颜色的全部替换为变量 */
/* 其它的就不列举了 */

记录一下 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
// 变量设定部分
@mixin light {
--color-mode: 'light';
--color-bg: #fffffd;
}

@mixin dark {
--color-mode: 'dark';
--color-bg: #2f3136;
}

body[data-theme='auto'] {
@include light();

@media (prefers-color-scheme: dark) {
@include dark();
}
}

body[data-theme='light'] {
@include light();
}

body[data-theme='dark'] {
@include dark();
}

// 变量使用部分
body {
background-color: var(--color-bg);
}

HTML

用 Typecho 博客举例,修改 header.php 文件

因为 php 可以和 Cookie 交互,所以用户选择的主题保存到了 Cookie 中(如果不用 PHP 的话,可以使用 JS 来实现,数据也可以储存到 localStorage)

<body> 处添加 php 代码

1
2
3
4
5
6
7
8
9
10
11
<?php
if($_COOKIE["data-theme"]=='light') {
echo "light";
}
elseif($_COOKIE["data-theme"]=='dark'){
echo "dark";
}
else{
echo "auto";
}
?>

完整的写法,如果 Cookie 里没有值,默认是第一次进入,选择 auto,否则主题由 Cookie 里面的值决定

1
2
3
4
5
6
7
8
9
10
11
<body data-theme="<?php
if($_COOKIE["data-theme"]=='light') {
echo "light";
}
elseif($_COOKIE["data-theme"]=='dark'){
echo "dark";
}
else{
echo "auto";
}
?>">

JavaScript

使用 Typecho 博客的话,可以放到 footer.php</body> 标签前面

或者保存为 js 文件外部引用,然后就完工了。

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
// Cookie过期时间为一个月
cookiesExp = new Date(new Date().setMonth(new Date().getMonth() + 1))

// 获取当前系统模式
function getNowTheme() {
let nowTheme = document.body.getAttribute('data-theme')
if (nowTheme === 'auto') {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
} else {
return nowTheme === 'dark' ? 'dark' : 'light'
}
}

// 手动切换主题模式
var navFn = {
switchDarkMode: function () {
let nowTheme = getNowTheme()
let domTheme = document.body.getAttribute('data-theme')

// 如果当前为自动模式,切换至用户选择的模式
if (domTheme === 'auto') {
let theme = nowTheme === 'light' ? 'dark' : 'light'
document.body.setAttribute('data-theme', theme)
document.cookie =
'data-theme=' + theme + ';path=/' + ';expires=' + cookiesExp

// 如果当前为light模式,则切换至dark模式
} else if (domTheme === 'light') {
document.body.setAttribute('data-theme', 'dark')
document.cookie = 'data-theme=dark;path=/' + ';expires=' + cookiesExp

// 如果当前为dark模式,则切换至light模式
} else {
document.body.setAttribute('data-theme', 'light')
document.cookie = 'data-theme=light;path=/' + ';expires=' + cookiesExp
}
},
}

// 跟随系统切换主题模式
var autoFn = {
switchAutoMode: function () {
document.body.setAttribute('data-theme', 'auto')
document.cookie = 'data-theme=auto;path=/' + ';expires=' + cookiesExp
},
}

简单的一个文字引用

1
2
<a class="darkmode_switch" onclick="navFn.switchDarkMode()">Dark</a>
<a class="auto_switch" onclick="autoFn.switchAutoMode()">Auto</a>

参考

更新深色模式主题并支持 prefers-color-scheme,作者:DSRKafuU