阅读 369

仅需几行代码,为你的网站添加黑暗模式 🌗

前言

在上一年,黑暗模式的概念席卷而来,随着系统级别的支持,其他主流应用程序的适配也陆续展开,它们大多提供了相应的入口,让用户可以切换整个主题,以此获得最舒适的体验

为什么要使用黑暗模式?

这个问题,我刚开始也不明白,黑乎乎的界面不仅难辨识,而且还加重开发和UI的负担,但回过头想一想,为什么 macOS,iOS 还引入了黑暗模式,Chrome、Gmail 等主流应用还提供支持黑暗模式的特性? 其背后还是有着一定的思考空间:

  • 护眼!没错,就是传说中的护眼。在夜晚,能够让用户的眼睛较为舒适,提高可视性
  • 可大幅减少耗电量(具体取决于设备的屏幕技术)
  • 一些特殊场景下,提高用户的体验度(小说阅读,视频观看)

Dark+Light mode

通过 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、伪类、属性选择器 > 元素、伪元素选择器

参考资料:

关注下面的标签,发现更多相似文章
评论