本站为个人非经营性网站,内容仅代表个人观点与记录,不构成任何专业建议。详见免责声明。

深色模式实践:prefers-color-scheme 与 CSS 变量

@media (prefers-color-scheme: dark) {
  :root {
    --bg:  #14161a;
    --ink: #e4e4e4;
  }
}

这是我给站点加深色模式时写下的全部内容。两个变量,六行,不到十分钟。

我以为换掉两个颜色就够了

整站配色都走 CSS 变量,我把深色模式理解成「同一组变量换一套值」:背景从 #fbfaf7 换成 #14161a,正文色从 #1b1b1b 换成 #e4e4e4,剩下交给变量自己去传导。

过程确实顺。刷新之后,正文、标题、页脚都对上了。

然后就发现不对。那天晚上关着灯,屏幕亮度拉到最低翻了两页,总觉得哪里别扭。

出问题的是链接色

强调色 --accent 在浅色下是 #2f6f6b,一个偏灰的青绿,和浅色背景的对比度大约 5.5:1,在白底上一眼能看出是链接。同一个值搬到 #14161a 上,掉到 3.1:1。

这个数字是我后来才算的。当时只觉得发闷,像屏幕没擦干净。

麻烦在于它不是那种会跳出来喊自己有问题的错。灰绿压在近黑的底上安安静静。那天我要是没把亮度调低,这个值大概会一直留着。

修法很简单:深色那套单独换一个更亮的 #6fb3ae,对比度回到 7.6:1。

顺带想清楚一件事。--accent 在站里不只当文字色,它同时是免责声明条左边那条竖线和页脚链接的悬停色。当线用的时候 3.1:1 完全够——线不是文字,不需要被读。一个变量担着两种职责,对比度要求却不一样。我最后没拆变量,只把深色那套整体提亮。

白闪,线索在滚动条上

第二个问题是切换前的那一帧白。系统是深色,打开页面先白一下再变黑。

我判断是样式表加载慢:浏览器先按默认渲染,CSS 到了再改过来。只对了一半。

线索在滚动条上。页面变黑之后,滚动条还是浅色的。滚动条的配色不归样式表管,那是浏览器控件的外观。它没跟着变,说明浏览器不知道这个页面支持深色,只知道 CSS 把某块背景刷黑了——所以在样式表到达之前,它按浅色把画布铺好了。

在 <head> 里加一行:

<meta name="color-scheme" content="light dark">

加完刷新,滚动条自己变深,白闪也基本没了。

为什么没做手动切换

很多站在页头放一个太阳和月亮的按钮。我做过半版,删掉了。

原因在存储。按钮如果记不住用户选过什么,翻一页就变回系统设置,等于没有;要记住,就得往 localStorage 里写一个值。

而这个站在隐私政策里对客户端存储的说法很紧:不用 Cookie,不用 localStorage 存个人信息,不接第三方统计。为一个记忆位去改那段文字,我不愿意;不改,就只能写一个会失忆的按钮。

还有一层:会去点那个按钮的人,多半是想要「系统是浅色,但我就想用深色」。

我的选择是不做。代价是上面那种需求得不到满足,算一处已知的欠缺。

还没弄明白的

加完 meta 之后,我又在 CSS 里写了 :root { color-scheme: light dark; },现在两处都在。我试过去掉其中一个,硬刷新二十次,白闪都没再出现。所以我其实不知道最后是谁修的——可能是 meta,可能是 CSS 那行,也可能第一次测的时候缓存没清干净。两处都留着,成本是一行。

接下来的打算

现在能看的:正文、代码块、表格、链接色、焦点轮廓、那个居中的备案区。完成度打七成。代码块注释在深色下和普通代码拉不开距离,我试了两版颜色都不满意,先放着。

下一步想接 prefers-contrast。不是因为它必须,是因为 3.1:1 那个教训:颜色的问题盯着看是看不出来的,得让工具替我算一遍。

本文为本人原创,首发于本站,采用 CC BY-NC-ND 4.0 许可,转载请注明作者「慎行」与原文链接。详见版权声明。

本文为个人学习与记录,不构成任何专业建议。