最可靠方式是用prefers-color-scheme媒体查询配合:root中的css变量;必须先在:root定义明亮模式默认值,再用媒体查询覆盖暗色值,否则未匹配时变量未定义导致样式失效。

纯 CSS 实现暗黑/明亮模式切换,最可靠的方式是用 prefers-color-scheme 媒体查询配合 :root 中的 CSS 变量——它不依赖 JavaScript,自动响应系统设置,且无闪烁、无 SSR 不一致风险。
用 @media (prefers-color-scheme: dark) 覆盖变量值
必须先在 :root 定义默认(明亮)变量,再用媒体查询覆盖暗色值。反过来写会导致未匹配时变量未定义,样式失效。
常见错误现象:页面全白或文字不可见,往往是因为把暗色变量写在了默认 :root 里,而没设明亮 fallback。
-
:root中只写语义化变量名,如--text-color,不带light/dark后缀 -
@media (prefers-color-scheme: dark)必须是顶层规则,不能嵌套在选择器内部 - 所有用到颜色的地方统一用
var(--text-color),避免硬编码重复
:root {
--bg-color: #fff;
--text-color: #333;
--border-color: #ddd;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #121212;
--text-color: #e0e0e0;
--border-color: #333;
}
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s, color 0.3s;
}
手动切换时为什么不能只靠 class 控制全部样式?
仅用 body.dark-theme 类切换,容易漏掉深层组件或第三方库样式;而 CSS 变量天然全局生效,var() 在任何层级都能响应变化。
例如一个卡片组件 .card 用了 background-color: var(--card-bg),只要 --card-bg 在 :root 中被媒体查询更新,它就自动变暗——不用给 .card 单独写 .dark-theme .card 规则。
- 变量方案更适合维护:改一处变量,全站颜色同步更新
- 类方案更适合需要用户强制覆盖系统偏好的场景(比如用户开了系统暗色,但想临时切回亮色)
- 两者可共存:用媒体查询设默认,再用 JS 切换
data-theme="dark"覆盖媒体查询
transition 加在哪才能让切换有渐变效果?
直接加在 body 或根元素上即可,但注意:只对支持过渡的属性有效,比如 background-color、color、border-color;而 filter、opacity 等需单独声明。
常见坑:给 html 或 :root 加 transition 没用,因为它们不是渲染盒,不触发视觉变化;必须加在实际渲染的元素(如 body)上。
- 推荐写法:
transition: background-color 0.3s ease, color 0.3s ease - 避免写
transition: all 0.3s,可能意外过渡其他无关属性(如font-size) - 如果用了
filter: invert(1)模拟暗色,它不支持transition,得换变量方案
真正难的不是写几行 CSS,而是决定哪些颜色该抽成变量、哪些组件要主动适配、以及是否允许用户覆盖系统偏好——这些决定了方案是“能跑”还是“能长期维护”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











