必须将明/暗色变量均声明在:root上,:root定义亮色值,[data-theme="dark"]覆盖暗色值;切换主题需操作document.documentelement而非body;预设html的data-theme并加css过渡可防闪动。

用 :root 和 [data-theme="dark"] 定义两套变量
必须把明/暗色值都声明在 :root 上,而不是只写一次再靠媒体查询覆盖——否则手动切换时无法生效。常见错误是只写 @media (prefers-color-scheme: dark) 里的一组变量,结果用户点按钮切主题,data-theme 改了但 CSS 没响应。
正确做法是:
-
:root定义默认(亮色)变量,如--bg-color: #fff -
[data-theme="dark"]覆盖同一组变量,如--bg-color: #1a1a1a - 不依赖
@media做切换逻辑,只用它做初始自动适配
为什么必须操作 document.documentElement 而不是 body
CSS 自定义属性的作用域由选择器决定。:root 等价于 document.documentElement(即 标签),所有子元素继承它的变量。如果改 body 的 data-theme,[data-theme="dark"] 选择器匹配不到 ,变量就不会重定义。
典型错误代码:document.body.setAttribute('data-theme', 'dark') —— 页面完全没变化。
正确写法只有一行:document.documentElement.setAttribute('data-theme', 'dark'),且值必须是小写字符串 'dark' 或 'light',不能是布尔值或首字母大写。
如何避免切换时的白屏或颜色闪动
闪动本质是“旧样式先渲染、新变量后生效”。关键有三点:
- 在
index.html的标签上预设data-theme值,比如根据localStorage或matchMedia同步结果直接写死,不让 JS 加载完才去改 - 所有用到颜色的地方加
transition,例如background-color 0.2s ease, color 0.2s ease - 避免在 JS 中批量修改多个 class 或 style,统一走
data-theme单点控制
漏掉预设 data-theme 是生产环境最常被忽略的闪动原因。
prefers-color-scheme 只负责初始判断,不监听变化
@media (prefers-color-scheme: dark) 在页面加载时生效一次,之后系统偏好变了(比如用户从设置里切深色),CSS 不会自动重算。想实时响应,得用 JS 监听:
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');<br>mediaQuery.addEventListener('change', e => {<br> if (e.matches) {<br> document.documentElement.setAttribute('data-theme', 'dark');<br> } else {<br> document.documentElement.setAttribute('data-theme', 'light');<br> }<br>});
注意:这个监听只在用户手动改系统设置时触发,和用户点击“切换主题”按钮是两回事,两者逻辑要分开处理,别混在一起写条件分支。
真正难的不是写几行 CSS 变量,而是让 data-theme 的设置时机、作用域、持久化和过渡效果全部对齐。任意一环断开,都会表现为“点了没反应”或“闪一下又变回去”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











