必须用:root声明css变量,因其是唯一全局可访问的作用域;写在class里会导致作用域受限、覆盖混乱或回退失效,且:root语义准确、优先级稳定、兼容ssr和多主题扩展。

主题切换必须用 :root 声明变量,不能只写在某个 class 里
很多人一开始把 CSS 变量写在 .dark-theme 或 .light-theme 类里,结果切换后样式不生效。根本原因是:CSS 变量作用域基于选择器,而主题需要全局覆盖,必须统一挂载在 :root 上——它等价于 html 元素,是整个文档的根级作用域。
正确做法是:先在 :root 定义默认值(比如浅色主题),再用媒体查询或 JS 切换时,重写 :root 下的同名变量:
:root {
--bg-color: #fff;
--text-color: #333;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1a1a1a;
--text-color: #eee;
}
}
如果要用 JS 主动切换,就直接操作 document.documentElement.style,而不是给 body 加 class 再写一堆嵌套规则。
JS 切换主题时,setAttribute 比 className 更干净
用 className 控制主题 class(如 body.className = 'theme-dark')看似直观,但会带来两个麻烦:一是要写大量 .theme-dark .button { --color: ... } 嵌套规则;二是容易和其它 class 冲突,比如框架自带的 dark 类或 Tailwind 的 dark: 前缀。
更稳妥的方式是用自定义属性标记主题状态:
document.documentElement.setAttribute('data-theme', 'dark')- 对应 CSS 写成
[data-theme="dark"] :root { --bg-color: #121212; }(注意:这里不是直接改:root,而是用属性选择器触发重写) - 或者更推荐——直接用 JS 设置内联样式:
document.documentElement.style.setProperty('--bg-color', '#121212')
后者无需额外 CSS 规则,变量更新立刻生效,也避免了选择器权重问题。
localStorage 保存主题偏好,但要注意首次渲染闪屏
用户选了深色模式,刷新页面又变回浅色,是因为 JS 还没执行,浏览器已按默认 CSS 渲染了一次。解决方法不是“等 JS 加载完再显示内容”,而是让 HTML 在服务端或初始 script 中就带上主题标记。
实操建议:
- 在
标签上加内联data-theme属性,值来自 localStorage 或服务端判断 - 用
<script></script>紧跟在里,读取 localStorage 并立即设置document.documentElement.style - 避免在
window.onload或 ReactuseEffect里才设置,那时 DOM 已渲染完毕,必然闪一下
示例关键代码:
<script>
const saved = localStorage.getItem('theme') || 'auto';
if (saved === 'dark') {
document.documentElement.style.setProperty('--bg-color', '#121212');
}
</script>
兼容性兜底:IE 不支持 CSS 变量,得有 fallback 方案
如果你的项目还要求支持 IE11,那纯 CSS 变量方案会完全失效。这时不能靠 @supports 来渐进增强——因为 IE 不识别该语法,直接跳过整段规则。
可行路径只有两条:
- 用 PostCSS 插件(如
postcss-css-variables)在构建时把变量静态替换为实际值,生成两套 CSS:一套给现代浏览器(含变量),一套给 IE(全展开) - 放弃 CSS 变量,改用预处理器(Sass/Less)+ 多输出主题文件,通过 link 标签动态切换
href
注意:不要试图用 JS 遍历所有元素去 setAttribute,性能差且维护成本高;也不要指望 CSS.supports('color', 'var(--c)') 在 IE 里返回 false——它根本不会执行这行代码。
真正难的不是怎么切主题,是怎么让变量在首次渲染、服务端、缓存、跨设备间保持一致。这些细节不处理好,用户会觉得“明明点了深色,怎么有时亮有时暗”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











