必须用 :root 声明全局 CSS 变量,因其特异性更高、跨文档兼容(SVG/Shadow DOM/SSR)、继承可靠;html 或 body 声明会导致变量不继承、旧 Safari 解析失败、@import 样式读不到、主题切换失效。

必须用 :root,不能用 html 或 body —— 否则变量大概率不继承、不生效、换肤失败。
为什么只能写 :root,html 声明会出问题
表面看 :root 和 html 都匹配 元素,但浏览器层叠规则里 :root 优先级略高(特异性同为 0-1-0,但规范强制它更靠前)。更关键的是兼容性:
-
html { --color: red; }在旧 Safari(iOS 12.2 之前)可能完全不解析变量 -
body { --color: red; }声明后,里的@import样式、<style></style>标签、甚至 SVG 的<fill></fill>都读不到 - 在 Shadow DOM、SVG 文档或 SSR 渲染中,
html选择器根本不会匹配,而:root始终有效
:root 块里怎么写才真正生效
语法错一个字符,整条声明就被忽略,且无报错提示。常见硬伤:
- 变量名必须以
--开头:--spacing-md✅,-spacing-md或spacing-md❌ - 每条声明末尾必须加分号:
--color-primary: #007bff;✅,--color-primary: #007bff❌(后续所有变量全失效) - 值必须合法:
--gap: 16px✅,--gap: 16❌(单位不能丢);--font-stack: "Inter", sans-serif✅,--font-stack: Inter, sans-serif❌(多值字符串必须加引号) - 不能嵌套引用未定义变量:
--text: var(--primary)且--primary还没声明 → 旧 Safari 直接跳过整条:root块
var() 引用时为什么“看起来没变色”
var() 不是函数,是运行时查找表达式。查不到就静默回退到属性默认值(比如 color 回退到 inherit,文字突然看不见),绝不报错。
立即学习“前端免费学习笔记(深入)”;
- 必须带 fallback:
color: var(--text-color, #333);✅,color: var(--text-color);❌ - fallback 必须是该属性的合法值:
font-size: var(--size, 1rem);✅,font-size: var(--size, auto);❌(auto不是font-size的合法值) - 拼写大小写严格敏感:
--ColorPrimary和var(--colorprimary)是两个独立变量 - 不要在
@keyframes中直接用var(--x)控制动画值,部分浏览器不支持
主题切换时 :root 变量该怎么组织
别把所有主题值堆在一个 :root 块里,也别依赖 @media (prefers-color-scheme) 自动覆盖——首次渲染可能取错值,且无法和用户手动切换共存。
- 基础变量放默认
:root::root { --color-bg: #fff; --color-text: #333; } - 暗色主题用属性选择器覆盖:
:root[data-theme="dark"] { --color-bg: #1a1a1a; --color-text: #eee; } - JS 切换时操作
document.documentElement:document.documentElement.setAttribute('data-theme', 'dark')✅,不是document.body❌ - 避免写
.dark-mode :root——:root永远是顶层元素,不可能是某个 class 的后代
最易被忽略的点:CSS 文件加载顺序决定变量是否可用。:root 块必须出现在第一个 <link rel="stylesheet"> 或 <style></style> 的最顶部,否则后面文件里写的 var(--x) 会因变量未注册而回退。


















