最稳的动态重色方案是用hsl()+var()配合@media(prefers-color-scheme:dark)完整覆盖所有hsl变量;必须重写全部分量(如--bg-h/--bg-s/--bg-l),仅改单一明度会导致对比度崩坏、safari静默失效,且需兼顾oled明度敏感性、wcag对比度验证、safari空格/引号兼容性、js+class双驱动防fouc及color-mix()合理使用。

直接用 hsl() + var() 配合 @media (prefers-color-scheme: dark) 覆盖变量,是最稳、最轻量的动态重色方案;但必须重写全部 HSL 分量,且单位和空格要严格合规,否则 Safari 会静默失效。
为什么不能只改 --bg-l 而要重写整套 HSL 变量
媒体查询只做“覆盖”,不是“合并”。@media (prefers-color-scheme: dark) { :root { --bg-l: 6%; } } 这种写法看似省事,但 --bg-h 和 --bg-s 仍沿用浅色值,结果可能是 hsl(210, 70%, 6%) —— 一个高饱和、极暗的蓝灰,对比度崩坏、文字难读。
- 深色模式下所有要用到的 HSL 分量(
--bg-h、--bg-s、--bg-l、--text-h、--text-s、--text-l)必须在@media块内完整声明 - OLED 屏对明度极敏感:
--bg-l: 4%和--bg-l: 8%视觉差异微弱,但前者更易引发眼疲劳;推荐深色背景明度控制在4%–8%区间 - 文字颜色不能只调
--text-l:需同步验证 WCAG 对比度。例如hsl(210, 10%, 20%)在hsl(210, 0%, 6%)背景上约 15:1,安全;若背景是hsl(0, 0%, 2%),同一文字只剩 7:1,必须把文字明度提到25%以上
hsl(var(--h),var(--s),var(--l)) 的单位与空格陷阱
旧版 Safari(尤其是 16.4 以下)对 hsl() 内部的 var() 解析极其苛刻,错一处就整条声明失效,不是报错,而是静默忽略。
-
--bg-s: 70%;✅,--bg-s: "70%";❌(引号让变量变成字符串,hsl()拒绝解析) -
hsl(var(--bg-h),var(--bg-s),var(--bg-l))✅(无空格最稳),hsl(var(--bg-h), var(--bg-s), var(--bg-l))❌(Safari 16.4 以下直接跳过整条规则) - 所有百分比值必须带
%单位,且不能加空格或引号;数字类如色相(--bg-h: 210)不带单位
用户手动切换时如何绕过 prefers-color-scheme 限制
点“深色模式”按钮时,系统偏好仍是浅色,@media 不触发,纯 CSS 无法响应。必须靠 JS 主动注入变量,并用 class 标记状态,否则 SSR 渲染和客户端样式脱节,出现 FOUC。
- 首次加载读取
window.matchMedia('(prefers-color-scheme: dark)').matches,存入localStorage;后续一切以该值为准,而非实时查媒体查询 - 切换时执行:
document.documentElement.style.setProperty('--bg-l', '6%'),同时给加class="user-dark" - CSS 中补一句:
.user-dark:root { --bg-l: 6%; }—— 这个 class 是服务端渲染和 JS 同步的关键信号,缺了就会闪屏 - 不要只靠 JS 改变量:没配套 class 的话,初始 HTML 的
:root还是浅色值,JS 注入后样式会抖一下
color-mix() 适合做自动适配的强调色,但别嵌套变量
color-mix() 不是 hsl() 的替代品,而是互补:它适合生成随背景自适应的边框、悬停态、强调色,避免 rgba() 在不同背景下发灰发脏。
- 显式声明色彩空间:
color-mix(in srgb, var(--accent) 70%, transparent 30%)✅,省略in srgb会静默失败 - 权重总和不必为 100%:
color-mix(in srgb, red 30%, blue 50%)会被浏览器自动归一化,等价于red 37.5%, blue 62.5% - 不支持变量嵌套计算:
color-mix(in srgb, var(--primary) var(--alpha), white 100%)直接报错;百分比必须是静态值或由构建工具提前算好 - 深色模式下,
rgba(--accent, 0.15)在#1e1e1e上几乎不可见;换成color-mix(in srgb, var(--accent) 20%, #1e1e1e 80%),明度差可控
真正麻烦的不是写几行 CSS,而是 HSL 分量之间相互牵制——调一个值,得同步验对比度、测 OLED 显示、查 Safari 兼容性、核对 localStorage 优先级。变量本身很轻,但让它稳稳落地,每一步都得踩准。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











