必须在:root中声明CSS变量,因其是规范唯一指定的全局锚点,特异性更高且强制继承,确保var(--x)在任意元素(含伪元素、Shadow DOM)中均可取值;html或body声明会导致继承失效、SSR不稳定或跨框架透传失败。

必须在 :root 中声明,且所有主题变量必须在默认 :root 块中完整定义——漏一个,页面一渲染就 fallback 到浏览器默认值,而不是“等后续覆盖”。
为什么只能用 :root,不能用 html 或 body
:root 是 CSS 规范唯一指定的全局锚点,特异性(0,1,0)略高于 html(0,0,1),更重要的是:它强制继承,确保 var(--x) 在任意元素(包括 <input>、<button></button>、Shadow DOM 内部)都能取到值。而 body { --x: red; } 声明的变量, 自身和 里的样式根本读不到;html { --x: red; } 虽然能用,但在 SSR 或某些 polyfill 场景下继承链可能断裂。
常见错误现象:
-
html上写了--color-bg,但<input>背景色仍是白色 → 因为表单控件未被html显式继承 - 用了
all: unset的组件内var(--spacing-md)失效 → 只有:root能穿透这类重置
:root[data-theme="dark"] 覆盖时必须满足三个条件
单纯写多个 :root 块不等于主题切换成功。生效依赖层叠顺序 + 完整声明 + 属性设置方式。
立即学习“前端免费学习笔记(深入)”;
- 变量名必须完全一致(大小写敏感):
--text-primary和--Text-Primary是两个变量 - 所有用到的变量必须在默认
:root中声明,哪怕深色模式下值相同也不能省略(例如--shadow-sm) - JS 切换必须用
document.documentElement.setAttribute('data-theme', 'dark'),不能用className = 'dark'—— 后者会清掉其他 class(如用于 A/B 测试或 SSR 标记的 class) - 媒体查询中的
@media (prefers-color-scheme: dark) { :root { --bg: #121212; } }不可靠:首次渲染可能取错值,且与用户手动切换冲突
var() 失效的真正原因不是语法错,而是这三点
浏览器不会报错,只会静默 fallback 到属性默认值(比如 color 变成 inherit,margin 变成 0),排查时容易绕弯。
- 拼写错误或大小写不一致:声明了
--spacing-xs,却写了var(--spacing-s)或var(--SpacingXS) - 声明顺序错:CSS 文件里
.card { color: var(--text); }出现在:root块之前 → 渲染时变量尚未定义 - 值非法:比如
--font-size: 16缺单位,或--size: calc(1rem * 2)写成--size: 1rem * 2(CSS 不支持裸运算)
最易被忽略的一点:var(--x, #fff) 的 fallback 必须写实。不写 fallback,一旦变量未定义,样式就崩,而且控制台毫无提示。


















