必须用:root声明CSS全局变量,因其是规范唯一认可的文档根锚点,覆盖全树且跨文档兼容;html或body声明会导致SVG不匹配、伪元素不可访问、层叠失效,且语法容错极低,漏分号或空格即整块静默失效。

因为只有 :root 是浏览器规范认可的、真正覆盖整个文档树的全局作用域锚点,其他位置声明的变量都会在某些场景下静默失效。
为什么不能用 html 或 body 声明全局变量
表面看 html { --color: red; } 也能读到,但实际有硬伤:
-
html在 SVG、XML 等非 HTML 文档中完全不匹配,:root才是规范定义的“文档根元素”锚点 -
body { --x: red; }声明后,<head>里的@import样式、<style>块、甚至伪元素(::before)都拿不到这个变量——继承链没挂到顶端 - 层叠顺序上,
:root比html更靠前;JS 动态设置style或 SSR 场景下,同名变量会被:root覆盖,html反而容易失效
:root 声明的语法细节:漏一个分号就全丢
写对位置只是第一步,语法错一点,整块变量静默失效(浏览器不报错):
- 每个声明末尾必须加分号:
--spacing-sm: 8px;✅,--spacing-sm: 8px❌(后续所有变量全丢) - 冒号前后不能有空格:
--color: #000✅,--color : #000❌ - 字符串值必须加引号:
--font-family: "Inter", sans-serif;✅,--font-family: Inter, sans-serif;❌(空格截断,只剩Inter) - 不能拆单位:
--size: 16;和--unit: px;无法组合,font-size: var(--size) var(--unit);是非法语法
var() 引用时 fallback 不是可选,而是容错刚需
裸用 var(--text-color) 很危险,变量未定义、拼错、或被重置为 unset 时,浏览器会回退到继承值甚至 initial,文字可能突然变透明或不可见:
立即学习“前端免费学习笔记(深入)”;
- 必须带 fallback:
color: var(--text-color, #333); - fallback 必须是当前属性合法值:
font-size: var(--size, 1rem);✅,font-size: var(--size, auto);❌ - 拼错变量名不报错:
var(--primry-color)(少个a)→ 直接用 fallback 或初始值,控制台零提示 - 不支持嵌套:
var(--color, var(--fallback))整个失效,最多只允许一层 fallback
主题切换必须用 data-theme,别信 @media 嵌套
@media (prefers-color-scheme: dark) { :root { --bg: #121212; } } 看似省事,但实际会出两类问题:
- 首次渲染取错值:页面加载快于系统通知,暗色变量根本没生效
- 与手动切换冲突:用户点了“亮色”按钮,但系统是暗色,两者变量同时存在,CSS 层叠规则让后声明的胜出,逻辑混乱难调试
- 正确做法是统一收口:
:root { --bg: #fff; }+:root[data-theme="dark"] { --bg: #121212; },再由 JS 执行document.documentElement.setAttribute('data-theme', 'dark')
动态更新只能走 style.setProperty();直接改 cssText 会清空所有已有自定义属性。最易被忽略的是:变量名大小写敏感,var(--Text) ≠ var(--text),且 :root 外层包裹漏掉就会被浏览器静默忽略。


















