深色模式与多主题
css 变量
本文章多使用 css 变量方式实现多主题和深色模式,因此请先了解 css 变量,如果对 css 变量不了解,请先阅读这篇文章《css 变量》
跟随系统设置
使用 CSS 媒体查询匹配系统设置。prefers-color-scheme 用于检测用户是否有将系统的主题色设置为亮色或者暗色。
css
// 用户选择选择使用浅色主题的系统界面
@media (prefers-color-scheme: light) {
}
// 用户选择选择使用深色主题的系统界面
@media (prefers-color-scheme: dark) {
}
// 表示系统未得知用户在这方面的选项
@media (prefers-color-scheme: no-preference) {
}
使 JavaScript matchedMedia API 匹配操作系统主题。如果对 window.matchMedia 不了解,请先看这篇文章《window.matchMedia 响应分辨率和操作系统主题模式变化》
js
function onOperateSystemChange(prefersDarkScheme) {
if (prefersDarkScheme.matches) {
// 用户操作系统主题设置为 dark
} else {
// 用户操作系统主题设置为 非dark
}
}
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)')
// 添加监听
prefersDarkScheme.addEventListener(onOperateSystemChange)
// 删除监听
prefersDarkScheme.removeListener(onOperateSystemChange)
深色、浅色模式, 多主题的实现
关于如何根据一个主题色使用 JS 生成一系列的次级衍生色,请见这篇文章:《chroma-js 进行颜色操作》
css
:root {
--color: #555;
}
:root[theme='dark'] {
--color: #fff;
}
body {
color: var(--color);
}
@media (prefers-color-scheme: dark) {
:root {
--color: #fff;
}
}
当 <html> 的属性 theme 的值不为 "dark" 时,var() 函数读取的是 :root{} 内的自定义属性(浅色模式匹配的的自定义属性),反之,则读取的是 :root[theme="dark"] 中的自定义属性。同样也可以结合媒体查询,实现跟随系统的效果
好处是扩展性更强,代码量较少,代码维护方便。不仅可以切换到深色模式,还可以切换到其他主题。 例如给 html 的 theme 属性设置其他值 <html theme="pink"> ,只需要添加下面这段 css:
css
:root[theme='pink'] {
--color: pink;
}
通过 JavaScript 给 <html> 的 theme 属性赋值为 "pink" ,就能切换到该主题。
js
const button = document.querySelector('.toggle')
button.addEventListener('click', function () {
document.documentElement.setAttribute('theme', 'pink')
})
css 变量命名
--[attribute]-[element]-[elementAttribute]-[x]: [value]
粗粒度命名
css
:root {
--primary: #0097ff;
--secondary: #6c757d;
--success: #28a745;
--info: #17a2b8;
--warning: #ffc107;
--danger: #dc3545;
--color-basic-50: #ffffff;
--color-basic-75: #fafafa;
--color-basic-100: #f5f5f5;
--color-basic-200: #eaeaea;
--color-basic-300: #e1e1e1;
--color-basic-400: #cacaca;
--color-basic-500: #b3b3b3;
--color-basic-600: #8e8e8e;
--color-basic-700: #6e6e6e;
--color-basic-800: #4b4b4b;
--color-basic-900: #2c2c2c;
}
细粒度命名
css
:root {
--color-text-primary: #555;
// ...
}
:root {
--color-notifications-button-hover-text: var(--color-text-primary);
// ...
}
图片处理
css
img.dark {
filter: brightness(0.8) contrast(1.2);
}
- brightness 使图像看起来更亮或更暗
- contrast 调整图像的对比度

阴影处理
让设计师给出不同主题的 shadow
主题切换过渡动画
使用 CSS 伪元素创建一个带有过渡效果的蒙层,在切换时给文档的根元素添加一个 class,通过给此 class 添加伪元素以创建带有过渡动画的蒙层。例如,在浅色切换到深色时 class 为 light-to-dark,反之,为 dark-to-light。
scss
$mode: () !default;
$mode: map-merge(
(
bg-light: #fff,
bg-dark: #252528,
),
$mode
);
$bg-light: map-get($mode, bg-light);
$bg-dark: map-get($mode, bg-dark);
.dark-to-light:after {
content: '';
width: 100vw;
height: 100vh;
position: fixed;
z-index: 99999;
left: 0;
top: 0;
margin-left: 0;
background-color: $bg-dark;
opacity: 0.7;
animation: toLight 1s linear 0s forwards;
// pointer-events: none;
}
.light-to-dark:after {
content: '';
width: 100vw;
height: 100vh;
position: fixed;
z-index: 99999;
left: 0;
top: 0;
margin-left: 0;
background-color: $bg-light;
opacity: 0.7;
animation: toDark 1s linear 0s forwards;
// pointer-events: none;
}
@keyframes toLight {
0% {
background-color: $bg-dark;
opacity: 0.7;
}
100% {
background-color: $bg-light;
opacity: 0;
}
}
@keyframes toDark {
0% {
background-color: $bg-light;
opacity: 0.7;
}
100% {
background-color: $bg-dark;
opacity: 0;
}
}
在切换模式时,将会在页面顶层展示带有对应过渡效果的蒙层。在过渡效果显示时,用户的鼠标无法点击页面的元素,这样做同时实现了类似防抖的效果。如果想移除这个效果,只需给蒙层加上 pointer-events: none;
储存状态
仅仅通过点击按钮切换主题还不够,应该将主题保存起来。否则,用户刷新页面或者再此进入页面将回到初始主题。在切换主题或者初始化时都应该使用状态储存。
js
localStorage.setItem("theme", <"dark" | "light">); // 存储
localStorage.getItem("theme"); // 读取
灰度模式
使用 css filter, 给网站根元素设置filter: grayscale(100%);,整个网站就变为灰颜色了
如果需要兼容 IE
css
html {
-webkit-filter: grayscale(100%);
filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
}
灰度效果