不考虑滤镜等奇技淫巧。
样式分离切换
<html class="dark">通过类名更改切换。tailwindCSS、windiCSS 使用的方法。掘金插件是放在<body>。 可以结合 CSS 变量使用。
body {
color: var(--text-color);
--text-color: black;
}
.dark body {
--text-color: white;
}
- 分别写两套样式,通过
<link herf="dark.css">切换。 - 如果只需要跟随系统,用 CSS 媒体查询即可:
@media (perfers-color-scheme: dark) {}
用户偏好 > 系统偏好
把用户偏好分为三类以上:
- 跟随系统,OS
- 夜间,Dark
- 日间,Light
- 跟随日月升落,Time(少见需求,忽略)
也可以使用 js, 结果是一个 MediaQueryList 对象:
window.matchMedia('(prefers-color-scheme: dark)')
// {media: '(prefers-color-scheme: dark)', matches: true, onchange: null}
下面介绍一种渐进式(优先满足用户偏好,用户没有偏好时跟随系统)的设计:
type ColorScheme = 'OS' | 'Dark' | 'Light'
const colorScheme: ColorScheme = localStorage.getItem('colorScheme') || 'OS'
const setColorScheme = (scheme: ColorScheme) => {
locolStorage.setItem('colorScheme', scheme)
switch (scheme) {
case 'OS':
// 这样写其实是优先 Light
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
// 监听系统色彩主题变化
window.matchMedia('(prefers-color-scheme: dark)').onchange = () => {
document.documentElement.classList.toggle('dark')
}
break;
case 'Dark':
document.documentElement.classList.add('dark')
// 移除监听
window.matchMedia('(prefers-color-scheme: dark)').onchange = null
break;
case 'Light':
document.documentElement.classList.remove('dark')
window.matchMedia('(prefers-color-scheme: dark)').onchange = null
break;
}
}
setColorScheme(colorScheme)
初始化
如果页面渲染时默认主题刚出现就被替换,产生刷新效果,影响用户体验,称 FOIT(flash of incorrect theme)。
为避免 FOIT,可把样式初始化代码写到 <head> 标签里面。
SSR
同构,或可考虑后端存储一下。
但无论如何,用户如果选择“跟随系统”,FOIT 就有 50% 几率出现了。 (没用的避免方法:可以考虑在“跟随系统”的情况下,先加一个额外启动页,把媒体查询的结果发回去再路由到原页面。)