主题变量必须定义在 :root 中,因为 CSS 自定义属性依赖继承,只有 :root 能确保全局可访问;@media 仅作兜底,需用 JS 切换 .dark 类并同步 localStorage,避免闪屏和样式错乱。

必须把所有主题变量定义在 :root 里,否则切换必然失效——这不是可选项,是 CSS 变量作用域的硬性限制。
为什么只能在 :root 中声明主题变量
CSS 自定义属性靠继承传播,而只有 :root(即 元素)能确保任意后代元素通过 var(--bg-color) 正确读取。写在 .dark、body 或组件类里,变量只在该选择器匹配的子树生效。
-
.theme-dark { --bg-color: #121212; }→button或input若未被该类包裹,就 fallback 到透明或浏览器默认值 -
body { --text-color: #eee; }→var(--text-color)在上求值时根本找不到定义 - Shadow DOM 内部不会继承外部
:root变量,需在:host或::slotted里手动传入
@media (prefers-color-scheme: dark) 不能替代 JS 主动切换
媒体查询只在页面加载和系统设置变更时触发,它不响应用户点击,也不支持 localStorage 持久化:刷新后状态丢失。旧版 Safari(iOS 13 以下)对它的支持也不稳定。
- 系统设为浅色,但用户想在你站用深色 →
@media完全无响应 - 正确做法是把它当作“默认兜底”,再用
.dark类覆盖同一组变量 - 靠 JS 控制
document.documentElement.classList.toggle('dark'),所有变量值由 CSS 规则自动重算
初始化 class 必须早于首屏渲染,且联动 localStorage
页面闪屏(白底→黑底)90% 是因为 class 没在 DOM 加载完成前设置。初始化逻辑必须三步到位:
- 读取
localStorage.getItem('theme'),而非依赖matchMedia初始值 - 在
<script></script>标签内直接操作document.documentElement.classList,不要等DOMContentLoaded - 切换按钮点击后,同步执行
classList.toggle('dark')和localStorage.setItem('theme', newTheme)
别用 style.setProperty() 手动设变量
调用 document.documentElement.style.setProperty('--bg-color', '#121212') 看似简单,实际会破坏整套机制:
- 覆盖
@media和.dark的声明逻辑,导致系统切换失效 - 大量变量逐个设置会卡 UI 线程,尤其在低端设备上明显
- 维护成本爆炸:新增一个颜色就得同步改 JS 和 CSS 两处
真正轻量的做法是只操作 class;漏掉任一环,用户刷新后主题就回退,或者原生控件(、)样式错乱——这是最容易被忽略的细节。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











