必须将所有主题变量定义在 :root 中,否则切换必然失效;css 自定义属性只在样式表解析时求值,不监听 class 或属性变化,需配合 :root.dark 等覆盖规则、正确初始化、持久化及组件样式引用 var() 才能正常工作。

必须把所有主题变量定义在 :root 里,否则切换必然失效——这不是可选项,是 CSS 变量作用域的硬性限制。
为什么只改 class 或 data-theme 页面没反应
CSS 自定义属性不会监听 HTML 属性或 class 变化,它只在样式表解析时求值。你执行了 document.documentElement.classList.toggle('dark'),但若 CSS 中没写 :root.dark { --bg-color: #121212; } 这类覆盖规则,变量值根本不会变。
- 检查 DevTools → Elements →
:root节点,确认切换后变量实际值是否更新 - 确保覆盖规则写在
:root默认声明之后,且选择器能匹配到(比如用:root.dark,不是.dark) - Tailwind 用户必须配置
darkMode: 'class',否则dark:bg-gray-800根本不会编译进 CSS - Bootstrap 5.3+ 需调用
bootstrap.Theme.getOrCreateInstance().update(),否则按钮、表单等组件颜色卡住不更新
:root 必须先声明默认值,不能只靠 @media 或 .dark
如果只在 @media (prefers-color-scheme: dark) 里定义变量,而没在顶层 :root 中设默认值,系统不支持该查询或设为 no-preference 时,var(--bg-color) 会 fallback 到透明或浏览器默认色,页面直接白屏或文字不可读。
- 所有变量必须先在
:root中声明,例如:--text-primary: #1a1a1a; -
@media块只能放在所有:root声明之后,且不能嵌套在其他媒体查询内 - 漏定义某个变量(比如
--border-color),而组件用了border: 1px solid var(--border-color, #ccc),深色下就会显示亮灰边框 - Shadow DOM 内部不继承外部
:root变量,需在:host或::slotted里手动重声明
JS 切换必须操作 document.documentElement,且同步 localStorage
操作 body 或任意子容器的 class / data 属性,对 :root 变量完全无效。闪屏、刷新回退、原生控件(<input>、<select></select>)颜色不一致,90% 都源于初始化时机或持久化缺失。
- 切换动作必须是:
document.documentElement.classList.toggle('dark')(Tailwind)或document.documentElement.setAttribute('data-theme', 'dark')(通用) - 初始化脚本不能等
DOMContentLoaded,要直接写在<script></script>标签内,早于首屏渲染 - 读取主题必须优先从
localStorage.getItem('theme')获取,而不是依赖matchMedia初始值 - 点击按钮后,必须同步执行 class/data 设置 +
localStorage.setItem('theme', newTheme),缺一不可
最容易被忽略的是:变量本身不支持动画过渡,var(--bg-color) 的变化是瞬时的;若要颜色渐变,得靠 JS 控制 class 切换 + 在真实属性(如 background-color)上加 transition。还有,所有组件样式必须用 var(--x) 引用,硬写颜色值等于放弃整套机制。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











