<p>必须使用 :root 声明 CSS 自定义属性才能全局生效,因其特异性高、语义明确且兼容 Shadow DOM;变量需顶层声明、-- 开头、正确引用并配合 data-theme 实现可靠换肤。</p>

直接在 :root 里声明 --color-primary 这类变量,就能全局生效——但前提是写对位置、命名规范、引用方式正确,否则子元素根本读不到,或者换肤时完全不响应。
为什么必须用 :root 而不是 html 或普通 class
浏览器强制保证 :root 下的自定义属性可被所有后代继承,且特异性(0,1,0)高于 html(0,0,1),在 Shadow DOM 或嵌套组件中更可靠;html { --x: red } 虽能工作,但语义弱、SVG/XML 不通用;写成 .theme-root { --x: red } 则完全无效——自定义属性只能由伪类或元素选择器声明,不能靠 class 触发。
:root 变量必须写在顶层,不能嵌套
常见错误是把变量塞进 @media、@layer 或某个 class 里,比如:
:root .header { --spacing-md: 16px; } /* 错误:这不是 :root 声明,是后代选择器 */正确写法只有一种:
立即学习“前端免费学习笔记(深入)”;
:root {
--color-primary: #007bff;
--space-md: 1rem;
--radius-sm: 4px;
}- 变量名必须以
--开头,primary-color合法,primaryColor也合法,但全项目要统一风格 - 不能写在 CSS 文件最外层无选择器处:
--color: red;直接被浏览器忽略 - 值支持链式引用:
--shadow-sm: 0 1px 2px rgba(0, 0, 0, var(--opacity-sm, 0.05));,但要避免循环依赖
主题切换时,data-theme 比 class 更可靠
给 <html> 添加 data-theme="dark",再用属性选择器覆盖变量,比 .dark-mode :root 安全得多——因为 :root 永远没有父元素,.dark-mode :root 实际上永远不匹配。
正确结构示例:
:root {
--color-bg: #fff;
--color-text: #333;
}
<p>:root[data-theme="dark"] {
--color-bg: #1a202c;
--color-text: #e2e8f0;
}- JS 切换主题应调用
document.documentElement.setAttribute('data-theme', 'dark'),而不是style.setProperty()——后者只改内联样式,无法触发[data-theme]选择器重计算 - 所有颜色/间距/圆角等设计令牌必须统一用
var(--x)引用,硬编码值(如#3b82f6)不会响应主题变化 -
var()必须带 fallback:color: var(--color-text, #333);,否则变量未定义时样式直接崩塌
跨页面复用变量时,别用 @import,用 <link>
把所有 :root 变量抽成独立文件 variables.css,然后在每个 HTML 的 <head> 中用 <link rel="stylesheet"> 引入;@import 会阻塞渲染,且在某些构建工具中导致变量作用域异常。
同时注意:
- 这个
variables.css必须加载在框架 CSS(如 Bootstrap、Ant Design)之后,否则你的--bs-primary覆盖不生效 - 变量名大小写敏感:
--Primary-Color和--primary-color是两个变量 - Safari 对
var()在calc()中的嵌套支持较弱,padding: calc(var(--space-md) * 2)在旧版 Safari 可能失败,建议预计算或加 fallback
最易被忽略的一点:第三方 UI 库若没开启 CSS 变量支持(比如 Element Plus 默认关闭),你改了 :root 也没用——得查文档确认是否提供 css-var 配置项,或手动 patch 样式。


















