:root 是定义全局 CSS 变量的唯一可靠位置,因其优先级高、语义明确、兼容性好(IE 不支持 html 声明),且仅在此处声明的变量才能被全文档通过 var() 访问;主题切换应通过类名覆盖 :root 变量而非 JS 批量设置。

:root 是定义全局 CSS 变量的唯一可靠位置,所有主题切换、动态样式更新都必须从这里出发。
为什么必须用 :root 而不是 html
:root 和 html 选择器在 HTML 中匹配同一个元素,但 :root 的优先级更高,且语义上明确表示“文档根作用域”——这是 CSS 自定义属性(--my-var)生效的最小全局作用域。用 html 定义变量在某些旧版浏览器(如 IE 全系)中完全不支持,而 :root 自 CSS3 起就是标准写法。
- 变量必须以两个连字符开头,例如
--bg-color,大小写敏感 - 只在
:root中声明才能被整个文档任意元素通过var(--bg-color)访问 - 若在其他选择器(如
body或.theme-dark)中重复声明同名变量,属于局部覆盖,不是“重新定义全局”
如何用 :root 实现深色/浅色主题切换
核心是:一份基础变量定义 + 多套主题类名覆盖 + JS 切换类名。不要试图用 JS 直接批量 setProperty 所有变量,维护成本高、易出错。
- 先在
:root中定义默认(浅色)主题变量::root { --bg-color: #ffffff; --text-color: #333333; --border-color: #e0e0e0; } - 再用带类名的选择器覆盖变量,例如:
.dark-theme { --bg-color: #121212; --text-color: #e0e0e0; --border-color: #333333; } - HTML 中只需切换
body的类:<body class="dark-theme">
或用 JS:document.body.classList.toggle('dark-theme'); - 所有用
var(--bg-color)的地方会自动响应新值,无需重写样式规则
var() 函数的回退值和常见误用
回退值不是“备选方案”,而是防错兜底。它只在变量未定义或值非法时生效,**不会因主题切换失败而自动 fallback**。
立即学习“前端免费学习笔记(深入)”;
- 正确写法:
color: var(--text-color, #000);—— 如果--text-color根本没声明,才用#000 - 错误写法:
color: var(--text-color, var(--fallback-color));—— 回退值不支持嵌套var(),会被当作字符串字面量处理 - 变量名拼错时(比如
var(--text-colr)),回退值立即生效;但控制台不会报错,只能靠肉眼排查 - 回退值不能是 CSS 函数调用(如
calc()、rgb()),除非整个表达式用引号包裹(不推荐)
JS 动态读写 :root 变量的注意事项
操作对象永远是 document.documentElement,不是 document.body 或其他节点。
- 设置变量:
document.documentElement.style.setProperty('--primary-color', '#ff6b6b'); - 读取变量(注意 trim() 去首尾空格):
const val = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); - 批量设置多个变量时,避免连续调用
setProperty,可先拼成字符串再一次性赋给style.cssText,减少重排 - 在 CSS 中使用
@media动态修改:root变量是安全的,但 JS 读取时需等媒体查询实际触发后才生效(可用matchMedia监听)
真正容易被忽略的是:CSS 变量的继承机制和级联优先级。局部作用域(如某个 class 内)声明的同名变量,会覆盖 :root 的值,但这个覆盖只对后代生效;父元素无法“穿透”子元素的局部变量去读取原始值。这意味着主题切换必须严格控制作用域层级,否则会出现部分区域换肤失败的情况。


















