前言
在上一年,黑暗模式的概念席卷而来,随着系统级别的支持,其他主流应用程序的适配也陆续展开,它们大多提供了相应的入口,让用户可以切换整个主题,以此获得最舒适的体验
为什么要使用黑暗模式?
这个问题,我刚开始也不明白,黑乎乎的界面不仅难辨识,而且还加重开发和UI的负担,但回过头想一想,为什么 macOS,iOS 还引入了黑暗模式,Chrome、Gmail 等主流应用还提供支持黑暗模式的特性? 其背后还是有着一定的思考空间:
- 护眼!没错,就是传说中的护眼。在夜晚,能够让用户的眼睛较为舒适,提高可视性
- 可大幅减少耗电量(具体取决于设备的屏幕技术)
- 一些特殊场景下,提高用户的体验度(小说阅读,视频观看)
通过 CSS 来支持黑暗模式
如果我们仅仅想针对黑暗模式,来更改网站的配色,那么 CSS 是一个不错的方法,前提是 系统开启了黑暗模式
在 CSS 文件中,写入以下媒体查询代码:
@media (prefers-color-scheme: dark) {
/* 黑暗模式下的样式代码 */
}
当系统开启黑暗模式后,媒体查询内的样式就会默认生效
PC 端实践
我们先创建一个 HTML文件,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
@media (prefers-color-scheme: dark) {
p {
color: red
}
}
</style>
<body>
<p>我在黑暗模式下会变红色</p>
</body>
</html>
打开浏览器,显示字是黑色的,没什么问题,接下来让我们对系统开启黑暗模式
这里以 windows 10 为例,我们只需在 开始-设置-个性化-颜色-选择默认应用模式(暗)
再次打开浏览器,我们的样式已经成功生效!
苹果电脑用户,从 macOS Mojava 版本起,也可开启黑暗模式
移动端实践
我们以 iPhone 为例,代码不变,只需在 设置-显示与亮度 开启黑暗模式即可,效果如图:
不足之处
根据 CanIUse.com(可以查询各浏览器的 CSS 属性支持情况)的数据显示:市面上的浏览器对该特性的支持率是 80%,IE 和 非 Chromium 内核版本的 Edge 不支持。所以如果你的网站面向 C 端,用户的浏览器各式各样,一定注意要做兼容处理
并且用户无法在浏览器上主动地切换模式,只能被动依赖于系统的主题模式
使用 JavaScript 切换样式表
使用 JavaScript 切换样式表来实现黑暗模式,我们需要创建两个不同的样式表,对应不同的主题(light and dark)
第一步,在 <head></head> 中插入默认样式表
<link id="theme" rel="stylesheet" type="text/css" href="light-theme.css" />
然后,创建一个按钮来切换样式表,为了能让用户快速地找到,应尽可能置于网页的头部位置
<button id="theme-toggle">Switch to dark mode</button>
继续添加以下 JavaScript 代码段:
// 当 DOM 加载完成后触发回调函数
document.addEventListener('DOMContentLoaded', () => {
const themeStylesheet = document.getElementById('theme');
const themeToggle = document.getElementById('theme-toggle');
themeToggle.addEventListener('click', () => {
// if it's light -> go dark
if(themeStylesheet.href.includes('light')){
themeStylesheet.href = 'dark-theme.css';
themeToggle.innerText = 'Switch to light mode';
} else {
// if it's dark -> go light
themeStylesheet.href = 'light-theme.css';
themeToggle.innerText = 'Switch to dark mode';
}
})
})
大家可以自行实践,这里就不展示了,那有没有优化的空间呢?
有!试想当用户切换到黑暗模式后,随后关闭了网站,当他们第二次访问时,又变成了默认主题,需要再次手动切换。不过,我们可以用 LocalStorage 来快速解决上述问题
在 localStorage 中保存用户的选择
LocalStorage 能存储键值对,如下所示:
localStorage.setItem('theme', 'dark-theme.css');
让我们对之前的代码做出一些优化:
// 当 DOM 加载完成后触发回调函数
document.addEventListener('DOMContentLoaded', () => {
const themeStylesheet = document.getElementById('theme');
const storedTheme = localStorage.getItem('theme');
if(storedTheme){
themeStylesheet.href = storedTheme;
}
const themeToggle = document.getElementById('theme-toggle');
themeToggle.addEventListener('click', () => {
// if it's light -> go dark
if(themeStylesheet.href.includes('light')){
themeStylesheet.href = 'dark-theme.css';
themeToggle.innerText = 'Switch to light mode';
} else {
// if it's dark -> go light
themeStylesheet.href = 'light-theme.css';
themeToggle.innerText = 'Switch to dark mode';
}
// 保存用户选择的主题
localStorage.setItem('theme', themeStylesheet.href)
})
})
这里不使用 sessionStorage 是因为 sessionStorage 的生命周期只存在于该域名下的标签页中,当标签页或浏览器关闭时,sessionStorage 会被清空,而 localStorage 不会,除非用户主动删除
题外话,大家知道 localStorage 的最大容量是多少吗?当超出最大容量,又会发生什么?有什么解决方案吗?
需要注意的是,localStorage 严格遵守 同源策略,你在通过 HTTP 访问站点时保存的主题,将会在通过 HTTPS 访问站点时消失
使用 JavaScript 切换类名
如果,我们只想用一个样式表,同样可以做到黑暗模式的切换
添加以下 JavaScript 代码段:
button.addEventListener('click', () => {
document.body.classList.toggle('dark');
localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
});
if (localStorage.getItem('theme') === 'dark') {
document.body.classList.add('dark');
}
CSS 文件,如下:
/* Light mode */
body {
background: #fff;
color: #000;
}
/* Dark mode */
body.dark {
background: #000;
color: #fff;
}
我们知道,CSS 样式的优先级取决于其选择器的权重叠加,哪个权重较大,就展示相应的样式:(body = 1) < (body.dark = 1 + 10 = 11)
!important > 行内样式 > ID 选择器 > Class、伪类、属性选择器 > 元素、伪元素选择器
参考资料: