设计令牌是css自定义属性(如--button-padding、--text-primary)的集合,代表颜色、间距等可复用的设计参数;它与bem分工协作——bem管结构归属,令牌管视觉值,不可用类名复用替代。

什么是设计令牌,为什么不能直接复用类名
设计令牌(Design Tokens)是 CSS 自定义属性(--button-padding、--text-primary)的集合,代表颜色、间距、圆角等可复用的设计参数。它和 BEM 的关系不是“替代”,而是分工:BEM 管归属与结构,设计令牌管视觉值。
常见错误现象:.card<strong>cta</strong> 和 .notificationaction 都写了 padding: 8px 16px,结果一处改,另一处漏;或者用 .btn 类强行复用,导致 .card__cta 一加 btn 就丢失 Block 上下文,JS 控制状态时类名冲突。
真正该做的,是让每个 Block 消费同一组变量,而不是共享类名:
- 设计令牌必须语义化,不带实现细节:
--cta-height✅,--padding-xs❌(尺寸是实现,不是意图) - 所有变量应声明在
:root或 Block 级作用域(如.card { --cta-bg: #007bff; }),避免全局污染 - 不要用
!important覆盖,变量天然支持层叠:.card { --cta-bg: #007bff; }→.card--inverted { --cta-bg: #fff; }
如何给 modifier 分配设计令牌
Modifier(如 --disabled、--loading)不是用来写样式的地方,而是触发设计令牌重写的开关。
使用场景:按钮在不同状态下需要改变背景、文字色、指针行为,但这些值应来自统一令牌,而非硬编码。
参数差异明显:
-
button--primary应设置--bg-color、--text-color,而不是直接写background: blue -
button--disabled只需重写部分令牌:button--disabled { --bg-color: #ccc; --text-color: #999; cursor: not-allowed; } - 不要写
button--disabled button--primary这种组合类——Modifier 必须互斥,靠 JS 清除旧类再加新类
容易踩的坑:
- 把
--loading当作视觉修饰符,实际它应由 JS 状态驱动,CSS 里只负责响应:button--loading { opacity: 0.6; }是错的,应该用--opacity-loading: 0.6并在 :root 或 Block 中定义 - 在 SCSS 中用
@if $modifier == 'disabled'动态插值生成 CSS —— 这会让构建后无法被 DevTools 直接定位,也破坏了令牌的可搜索性
第三方组件怎么接入设计令牌
Ant Design、Element Plus 这类库的类名(如 .ant-btn)不能直接套用你的 --cta-bg,因为它们不消费你的变量。
标准解法只有一条路径:用 wrapper + 局部作用域限定。
- 不要:
.card .ant-btn { background: var(--cta-bg); }—— 后代选择器违反 BEM,且一旦 Ant 升级类名就失效 - 正确:
.card<strong>trigger { --cta-bg: #007bff; }</strong>,然后在 wrapper 内通过:deep(.ant-btn)(Vue)或.cardtrigger .ant-btn(通用 CSS)消费变量
性能影响要注意:
-
:deep()是 Vue 特有编译期语法,不会增加运行时开销;但通用写法中,.card__trigger .ant-btn会提升选择器权重,建议只在必要时用,优先用内联 style 或组件 prop 透传 - 如果第三方组件支持 CSS 自定义属性(如 Mantine 的
--button-bg),直接覆写即可:.card__trigger { --button-bg: var(--cta-bg); }
为什么不能把设计令牌塞进 JS 对象里管理
有人把所有令牌存在一个 tokens.js 里,再用 JS 注入到 :root,看起来很“工程化”,但实际埋了三个雷:
- 构建产物里变量名被压缩(如
--cta-bg → --a),CSS 和 JS 不同步,DevTools 里查不到原始语义
- SSR 场景下,JS 还没执行,
:root 里没有变量,首屏渲染错乱
- 修改一个颜色要同时改 JS + CSS + 文档,违背“单一事实源”原则
--cta-bg → --a),CSS 和 JS 不同步,DevTools 里查不到原始语义:root 里没有变量,首屏渲染错乱真正稳定的方案是:所有设计令牌写死在 CSS 中(SCSS/PostCSS 生成也可),用 postcss-custom-properties 做降级,JS 只读取(getComputedStyle(document.documentElement).getPropertyValue('--cta-bg')),不写入。
复杂点在于变量作用域嵌套——比如 .card--featured 想局部覆盖 --cta-bg,必须确保它的声明位置在 .card 规则内,否则会被根作用域覆盖。这个细节,90% 的团队在 Code Review 里根本看不出来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











