直接在 :root 声明 --primary-color 会失控,因其混杂物理值、语义用途与品牌归属;应分三层:基础层(--color-blue-500)、语义层(--color-bg-surface)、组件层,靠作用域注入值而非命名变体。

为什么直接在 :root 里写 --primary-color 会失控
因为没分层。你写 --primary-color: #0067D1 看似干净,但一旦品牌要换蓝→绿,暗色模式要调饱和度,或第三方组件要复用你的间距体系,就会发现:这个变量既承担了“物理色值”,又暗示了“语义用途”,还绑定了“品牌归属”。结果就是改一处、漏十处,!important 满天飞,var(--primary-color) 在按钮里是背景,在图标里是描边,在禁用态里又得覆盖——根本不是变量,是硬编码别名。
- 基础 Token 必须只表达“物理事实”:比如
--color-blue-500、--space-4、--radius-md,不带任何业务语义 - 语义 Token(如
--color-brand-primary)必须和基础 Token 解耦,靠赋值关联,不能混写 - :root 中只声明空壳变量(带 fallback),实际值由
[data-theme]或媒体查询注入
如何定义三层 Token 命名结构
Token 不是颜色列表,是契约。三层结构让修改可控、扩展可预期:
-
基础层(Primitive):纯物理值,命名格式为
--{category}-{value},例如--color-blue-500、--space-8、--shadow-lg。这些值永不直接用于组件,只供上层引用 -
语义层(Semantic):按角色定义,命名格式为
--{domain}-{purpose},例如--color-bg-surface、--color-text-primary、--space-padding-base。它们在[data-theme="light"]或[data-theme="dark"]下批量重赋值 -
组件层(Component):组件内只读语义变量,例如
.btn { background-color: var(--color-bg-surface); };局部定制用属性选择器,如button[data-variant="ghost"] { --color-bg-surface: transparent; }
怎样让暗色模式真正“热切换”而不是 reload
关键不在 JS 切 class,而在 CSS 变量的注入时机与作用域。用 @media (prefers-color-scheme: dark) 是静态响应,用户手动切主题时无效;用 JS 批量 setAttribute 是可行路径,但必须避开两个坑:
- 不要逐个
document.documentElement.style.setProperty()—— 性能差、易遗漏、无法回滚;应一次性注入整个主题对象,例如Object.entries(theme).forEach(([k, v]) => root.style.setProperty(k, v)) - 务必在
:root上设 fallback:--color-bg-surface: #ffffff;,否则首次渲染或 JS 失败时页面白屏/黑屏 - 避免在媒体查询里重复声明整套语义变量;推荐用一个缩放控制变量,如
--color-scheme-scale: 1;,再用calc(var(--color-blue-500) * var(--color-scheme-scale))做动态映射(仅限支持 calc 的场景)
构建时要不要用 style-dictionary 或 tokens-studio
要看团队规模和交付节奏。小项目手写 JSON + PostCSS 插件足矣;中大型多品牌 SaaS 必须引入工具链,否则 Token 同步成本指数级上升。
- style-dictionary 支持导出为 CSS 变量、SCSS、Android XML、iOS Swift 等多端格式,适合跨平台统一管理
- tokens-studio 提供可视化编辑界面,适合设计师参与 Token 定义,但需注意它生成的变量名默认带前缀(如
color-primary-500),需配置 mapping 规则对齐你的三层命名规范 - 无论用哪种工具,输出的 CSS 必须保留
:root中的 fallback 值,且禁止压缩工具移除未使用的变量(Webpack 的css-minimizer-webpack-plugin需配discardUnused: false)
最常被忽略的一点:Token 的“变体”不是靠命名区分(如 --color-primary-dark),而是靠作用域隔离。同一个 --color-brand-primary 在 [data-theme="brand-a"] 下是蓝色,在 [data-theme="brand-b"] 下是绿色——变量名不变,值才变。这才是轻量级设计系统的真正支点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











