必须在:root中定义颜色变量,因其是css全局作用域,子元素自动继承;写在组件或body中会导致作用域受限或优先级不稳。每个var()调用必须带合法fallback值,否则变量未定义时整条css声明静默失效,引发ui崩塌。

必须在 :root 中一次性声明所有基础颜色变量,且每个 var() 调用都得带合法 fallback 值——漏掉任意一环,主题切换时就会静默失效,UI 崩塌毫无征兆。
为什么只能在 :root 里定义颜色变量
浏览器把 :root 当作 CSS 全局作用域,子元素自动继承,不需要写选择器重复声明。写在 .theme-dark 或组件类里,变量只在该局部生效;写在 html 或 body 上,优先级不如 :root 稳定,还可能被其他样式意外覆盖。
- 错误示范:
.button { --color-primary: #007bff; }→ 只对 button 内部有效,.header读不到 - 正确写法:
:root { --color-primary: #007bff; --color-text: #333; } - 所有变量必须有默认值:哪怕只支持浅色模式,也要先定义,否则深色模式覆盖时会因“未声明”而 fallback 失效
var(--x) 失效的三大高频原因
它不报错、不警告,值无效就直接跳过整条 CSS 声明,背景变透明、文字消失都是常态。DevTools 里看到 computed 样式是空的,基本就是这里翻车了。
- 没写 fallback:
color: var(--color-text);→ 变量未定义时,Chrome 回退 inherit,Firefox 可能渲染为 transparent - fallback 值非法:
background: var(--bg, #ggg);→#ggg不是合法颜色,整个 fallback 作废 - 在不支持的上下文中用:
content: attr(data-color);不能读变量;inline style(如<div style="color: var(--text)">)完全不解析 <code>var()如何让暗色模式真正响应切换
靠 JS 切换
data-theme属性最干净,但光改属性没用——CSS 里必须有对应规则覆盖变量值,否则变量还是旧的。- 基础色定义在
:root,暗色覆盖写在:root[data-theme="dark"]里,不是新建一套选择器 - JS 切换只需:
document.documentElement.setAttribute('data-theme', 'dark'),别用className操作 - 过渡要加在依赖变量的属性上,比如
background-color: var(--color-bg); transition: background-color 0.2s;——var()本身不触发 transition
大型项目里怎么避免变量名混乱
命名不是风格问题,是维护成本问题。语义化前缀 + 分层结构,才能保证“改一个主色,全站自动同步”,而不是满代码库搜
#007bff。- 用
--color-primary,别用--blue-500或--header-bg—— 前者绑定视觉,后者绑定组件,都不可迁移 - 中性色和功能色分开:
--color-text-primary、--color-border-divider、--color-success - 所有变量注释统一格式:
/* @token color-primary — 主按钮点击态 */,配合 PostCSS 插件做构建时校验
最常被忽略的是 fallback 的合法性:它不是“备用方案”,而是变量求值失败时的最后防线。写错一个字符,比如
#fff写成#ff,整个声明就归零——而浏览器连 warning 都不会抛。 - 基础色定义在











