必须用:root定义CSS变量,因其是规范唯一全局锚点,确保所有元素(含SVG、表单控件)稳定继承;若仅在局部类中声明,未命中该类的元素将fallback至初始值,导致页面局部失色或文字消失。

直接用 :root 定义语义化变量 + @media (prefers-color-scheme: dark) 覆盖,是最轻量、最可靠的方式。别写死颜色值,也别靠 JS 注入 style。
为什么必须用 :root 而不是局部 .dark 类覆盖?
因为 :root 是全局作用域,所有 var(--color-bg-primary) 都能稳定读取;而如果只在 .dark 里定义变量,未命中该类的元素(比如内联 SVG、第三方组件)就会 fallback 到初始值或透明——页面局部发白或文字消失就是这么来的。
常见错误现象:div { background-color: var(--bg-color); } 在暗色下仍为白色,检查发现 --bg-color 只在 .dark div 里定义,没上提至 :root。
-
:root里的变量天然可被全页面继承,无需重复声明 - 媒体查询中的
:root覆盖是 CSS 原生行为,无 JS 依赖、无渲染延迟 - 搭配
html { color-scheme: light dark; }后,原生表单控件(、)也会自动适配
@media (prefers-color-scheme: dark) 里只能改变量,不能写具体样式
写具体样式(比如直接给 body { background: #121212; })会和变量体系割裂:一旦你用 var(--color-bg-primary) 控制某块区域背景,而另一处又用硬编码 #121212,后期换主题时就得两头改,极易漏掉。
正确做法是把所有颜色逻辑收口到变量里:
:root {
--color-bg-primary: #ffffff;
--color-text-primary: #1a1a1a;
--color-border: #e0e0e0;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg-primary: #121212;
--color-text-primary: #e0e0e0;
--color-border: #333333;
}
}
body {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
border-color: var(--color-border);
}
这样改一套变量,全站响应;加新组件时也只需引用已有变量,不新增颜色值。
变量命名必须带语义,且明暗两套同名
像 --dark-blue-500 或 --bg-dark 这类命名,在项目大了之后根本没法维护——你不知道它用在哪、是否已被弃用、有没有和 --card-bg-dark 冲突。
应该按用途而非明暗状态命名:
- ✅
--color-bg-primary(主背景) - ✅
--color-text-secondary(次要文字) - ✅
--color-border-divider(分隔线边框) - ❌
--bg-dark(含义模糊,无法判断层级) - ❌
--text-gray-800-dark(绑定具体色值,失去语义)
关键点:明色和暗色两套 :root 必须使用完全相同的变量名。否则 var(--color-bg-primary) 在暗色环境下会 fallback 到未定义值,甚至触发浏览器默认颜色(比如白色),导致“切了暗色但部分区域还是亮的”。
避免透明度和 hsl() 混用导致发灰
很多人喜欢用 rgba(0, 0, 0, 0.1) 做蒙层,但在暗色下叠加多次后容易变脏;或者用 hsl(0, 0%, 90%) 表示浅灰,却在暗色里继续用同一 hsl() 值——结果是背景和文字对比度崩塌,可访问性失败。
推荐方案:
- 透明度单独抽成变量:
--opacity-overlay: 0.16;,然后组合使用background-color: hsl(0, 0%, 12%)+opacity: var(--opacity-overlay) - 深色文本不用
hsl(0, 0%, 10%),改用明确亮度值:--color-text-primary: hsl(0, 0%, 92%)(暗色下) vshsl(0, 0%, 12%)(亮色下) - 所有
hsl()值统一用百分比表示明度(%),避免小数或整数混用造成理解偏差
最后提醒一句:localStorage 存的是用户手动选择,prefers-color-scheme 是系统偏好,两者要共存但不能互盖。初始化时先读 localStorage,没值再 fallback 到媒体查询结果——这个逻辑 JS 里必须做,CSS 本身做不到。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











