必须在 :root 中定义基础令牌,所有变量以 -- 开头、语义化命名;:root 是唯一安全起点,确保全局继承;var() 必须带字面量 fallback;分基础令牌与语义映射两层解耦;js 换肤需批量注入主题对象。

必须在 :root 中定义基础令牌,且所有变量名统一用 -- 开头、语义化命名,否则设计系统会失控——不是不能跑,而是改一个色,三处组件漏更新,五处对比度不达标。
为什么:root是唯一安全的起点
把颜色、间距、圆角这些设计令牌塞进某个 class(比如 .theme-light)里,等于主动放弃“全局可继承”这个核心优势。子元素取不到变量,var(--spacing-md) 就会静默回退到 inherit 或默认值,页面可能突然塌陷或文字消失。
-
:root等价于html,但语义更清晰、优先级更高,IE11 起就稳定支持 - 别写
.tokens { --color-primary: #007bff; }—— 这个变量只在.tokens元素内有效 - 所有基础令牌(如
--color-primary-500、--space-xs、--radius-sm)必须在:root里一次性声明,默认值不可为空
var() 的 fallback 不是可选项,是保命线
写 color: var(--text-primary); 是危险操作。一旦 --text-primary 没定义(比如子应用没加载主题 CSS,或 JS 注入失败),Chrome 会继承父级文字色,Firefox 可能直接透明——用户看到的就是一片白或黑,且控制台不报错。
- 必须写成
color: var(--text-primary, #333);,fallback 值得是真实可用的 CSS 字面量,不能是另一个var() - 深色模式切换时,
--text-primary可能被覆盖,但 fallback 保证降级可用 - 微前端场景下,父容器和子应用变量作用域隔离,fallback 是唯一兜底手段
基础令牌 + 语义映射,才是可维护结构
直接拿 --color-blue-600 当按钮背景色用,等于把设计系统绑死在具体实现上。换品牌色?全项目搜索替换,连 svg fill 和伪类 ::placeholder 都得人工翻。
- 第一层:基础令牌(原始设计值),命名带层级,如
--color-primary-500、--color-gray-900 - 第二层:语义映射(用途导向),如
--color-text→var(--color-gray-900),--color-bg→var(--color-gray-50) - 修改主题时,只动第二层映射;调整视觉风格时,只调第一层基础值;两者完全解耦
JS 动态换肤必须批量注入,不能单点 setProperty
用 document.documentElement.style.setProperty('--color-primary', '#ff6b35') 手动改十来个变量,极易漏项、拼错名(--primary-color vs --color-primary),而且无法回滚、难测试。
- 把整套主题存为 JS 对象:
const themes = { light: { '--color-primary': '#007bff', '--color-bg': '#fff' }, dark: { ... } } - 批量设置:
Object.entries(theme).forEach(([k, v]) => doc.documentElement.style.setProperty(k, v)) - 配合 ESLint 插件
stylelint-custom-property-no-outside-root检查变量是否都在:root定义
真正容易被忽略的,是基础令牌的 HSL 拆分和衍生计算——比如 --primary-h、--primary-s、--primary-l,再用 hsl(var(--primary-h), var(--primary-s), calc(var(--primary-l) - 10%)) 生成悬停态。这比硬写十六进制色值更能守住视觉一致性,尤其在暗色模式下自动调亮/调暗时。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











