必须写在:root中,因其是唯一能保证全局继承和全元素可访问的声明位置;变量若写在.theme-dark等类下仅限子树生效,会导致组件引用时回退失效,且所有var()必须带fallback(如var(--color-text, #333))以防渲染异常。

直接在 :root 里定义语义化变量,配合 var(--color-primary) 引用,就能把“改主题”变成“改一组值”,而不是满项目搜 #3498db。
为什么必须写在 :root 而不是 .theme-dark 里
写在 .theme-dark 这类 class 下,变量只在该选择器匹配的子树生效。组件里调用 var(--color-primary) 会 fallback 到初始值或继承值,颜色就断了。只有挂到 :root(即 )上,才能被全页面所有元素读取。
-
:root是唯一能保证全局继承、全元素可访问的声明位置 - 别在组件里重复声明同名变量,比如
.button { --color-primary: red; }——这会覆盖根变量,且无法被兄弟组件复用 - 所有主题变量(哪怕浅色模式下值相同)都得在
:root和[data-theme="dark"] :root中完整定义,否则未声明的变量一渲染就丢色
var() 调用必须带 fallback,否则旧环境直接崩
没 fallback 的 var(--color-text) 在 IE 或部分旧 WebView 里解析失败,可能变成 transparent 或继承值,导致文字不可见、背景消失。这不是“不支持变量”的问题,而是回退逻辑缺失。
- 所有
var()必须写成var(--color-text, #333)这种形式 - fallback 不仅防兼容性问题,也防 JS 动态设置失败时的样式断裂
- DevTools 里看到变量值是
transparent或空,八成是 fallback 没写,或者setProperty()改的是未声明的变量名
命名不能用 --blue-500,得用 --color-btn-primary-bg
裸名如 --red、--c1 上线后根本没法 grep 或交接;--blue-500 这类设计系统残留名不告诉你这个色用在哪,换主题时还得翻 Figma 对照。
- 按角色命名:
--color-primary(主色调)、--color-surface-card(卡片底色)、--color-state-hover(悬停态) - 基础设计 token(颜色、字号、圆角)放全局,组件专属变量(如
--card-padding)应限制在组件作用域内:.card { --card-padding: 16px; } - 全局变量加语义前缀,禁止裸名;每个变量都要有 fallback
document.documentElement.style.setProperty() 不生效?先查这三处
调用 document.documentElement.style.setProperty('--color-bg', '#1a202c') 后没变化,大概率不是 JS 写错了,而是 CSS 层级或继承链断了。
- 没在 HTML 模板或内联 script 中预设初始
data-theme——浏览器先按默认 CSS 渲染,JS 执行后再切,必然 FOUC。正确做法:<script>document.documentElement.dataset.theme = localStorage.getItem('ui-theme') || 'light';</script>放在里 - 只调
dataset.theme = 'dark'却忘了localStorage.setItem('ui-theme', 'dark')——刷新后变回默认 - 用
style.setProperty()直接改--hue,但没在:root中预先声明该变量——浏览器找不到定义,var(--hue)回退为transparent或继承值,整页背景/文字瞬间消失
真正难的不是写变量,而是让变量始终处于可定位、可预测、可回退的状态。一旦变量名散落在不同文件、fallback 缺失、作用域错位,主题切换就会变成排查黑洞。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











