设计令牌是设计系统从文档走向工程的关键一步。本文从命名分层、语义映射、多主题治理、工具链同步、版本管理与团队协作等角度,拆解设计令牌如何真正落地,而不是停留在一次性的变量替换。
一个设计系统最容易断裂的地方,不在组件库的视觉稿,而在“设计稿上的颜色”到“代码里的值”之间那条通道。设计师改了品牌色,开发在几十个文件里搜索旧色值;产品要做暗色模式,发现颜色写死在组件内部;新来的工程师问“这个灰色该用哪个”,没有人能给出确定答案。设计令牌(Design Tokens)就是为了解决这条通道而存在的:它把颜色、字号、间距、圆角、阴影、动效时长等最小决策单位抽出来,命名为可被设计和代码共同引用的变量。
但设计令牌真正的难点,从来不是“把颜色变成变量”,而是让它在一段时间内保持稳定、可扩展、可治理。下面从结构、命名、映射、主题、工具链和协作几个层面展开。
令牌不是一个文件,而是一套三层结构
很多团队第一次做令牌,会直接建一个 colors.json,把品牌色、灰色、语义色全部混在一起。这种结构在单主题、小项目里能跑,一旦遇到暗色模式、多品牌或组件扩展,就会迅速失控。更可持续的做法是分成三层。
第一层是原始值(Primitive)。它只描述“这是什么颜色”,不描述用途。比如 blue-500、gray-100、space-4、radius-2。这一层不含任何业务语义,相当于一套调色盘和尺度表。
第二层是语义令牌(Semantic)。它描述“这个值用在哪里”。比如 text-primary、surface-raised、border-danger、action-hover。这一层是设计与代码之间的契约,组件只引用这一层。
第三层是组件令牌(Component)。它描述“某个组件在某个状态下的具体取值”。比如 button-primary-bg-default、input-border-focus、modal-shadow。这一层可以覆盖语义令牌,用于组件级微调。
层级 | 命名关注点 | 示例 | 谁维护 |
|---|---|---|---|
原始值 | 色相、明度、尺度 |
| 设计系统核心团队 |
语义令牌 | 用途、角色 |
| 设计系统 + 设计工程 |
组件令牌 | 组件、状态 |
| 组件维护者 |
三层结构的好处是:品牌更新只需改原始值和语义映射;暗色模式只需替换语义令牌的取值;组件升级不影响底层色板。层级越清晰,后期治理成本越低。
命名决定了令牌能不能被用对
令牌命名是设计系统里最容易被低估的工作。名字如果只描述外观,使用者就要靠记忆去猜;名字如果描述用途,使用者就能按意图查找。命名应遵循几条原则。
用角色,不用色相。
text-danger比red-500更稳定,因为品牌色可能变,语义不变。用层级,不用具体位置。
surface-raised比card-bg更通用。用状态,不用临时词。
hover、focus、pressed、disabled是固定后缀。用尺度,不用魔法数字。
space-4对应一个间距刻度,而不是margin-13px。用英文小写加连字符。保持跨语言、跨工具的一致性。
一个常见的命名模板是:
{类别}-{角色}-{属性}-{状态}例如:
text-primary-default
text-primary-disabled
surface-raised-default
border-danger-focus
button-primary-bg-hover命名不必追求绝对完整,但要保证同一类令牌遵循同一模式。团队可以接受“有点长”,但不能接受“同一种东西有三种叫法”。命名混乱会让令牌库迅速失去权威,开发宁愿写死数值。
语义映射是设计系统的核心资产
原始值只是原料,真正决定体验一致性的是语义映射。所谓映射,就是规定“哪个原始值在什么角色下被使用”。这一步做得好,主题切换、品牌换肤、暗色模式都只是替换映射表,而不是重做界面。
假设有一套中性色:
原始值 | 亮度 | 亮色模式角色 | 暗色模式角色 |
|---|---|---|---|
| 最亮 | 页面背景 | — |
| 很亮 | 卡片背景 | — |
| 偏亮 | 分隔线 | 边框 |
| 中间 | 辅助文字 | 辅助文字 |
| 偏暗 | 正文 | — |
| 很暗 | 标题 | — |
| 最深 | — | 页面背景 |
映射表应该由设计系统团队维护,并且有明确的“不要直接使用原始值”规则。组件代码里出现 gray-200 就是一种信号,说明语义层缺失或不够用。发现这种情况,应该补语义令牌,而不是允许例外长期存在。
多主题与暗色模式:令牌真正发挥价值的地方
暗色模式不是把颜色反过来,而是重新定义每个语义角色在暗背景下的取值。设计令牌让这件事从“逐个组件改样式”变成“替换一张映射表”。
亮色模式下,surface-raised 可能是白色,靠阴影表达层级;暗色模式下,阴影几乎不可见,层级要靠表面亮度差。于是 surface-raised 在暗色模式中可能变成比页面背景更亮一阶的深灰。类似的还有:
text-primary:亮色模式接近黑,暗色模式接近白但略降亮度,避免光晕。border-default:亮色模式偏浅,暗色模式需要提高对比度。action-primary:亮色模式用较深的品牌色配白字,暗色模式可能要提亮品牌色以保持对比。shadow-overlay:暗色模式中可能需要替换为边框或内发光。
多品牌场景同理。品牌 A 的主色是蓝,品牌 B 的主色是绿,但 action-primary、text-link、focus-ring 这些角色名不变,只是映射到不同的原始值。组件代码不需要知道品牌是谁。
工具链:令牌要能被设计工具和代码同时消费
设计令牌如果只存在代码里,设计师改稿时仍然可能用错颜色;如果只存在设计工具里,开发仍然要手动转换。理想状态是单一数据源,向两端同步。
常见的做法是用 JSON 或 YAML 作为源文件,通过构建脚本生成多端产物:
tokens/
primitive.json
semantic.light.json
semantic.dark.json
component.json构建后输出:
CSS 自定义属性:
--text-primary预处理器变量:
$text-primaryJavaScript/TypeScript 常量:
tokens.text.primaryiOS/Android 资源文件
设计工具可导入的变量集
这一步的工程化程度,决定了令牌能不能被日常使用。如果每次改令牌都要手动通知三端,团队很快就会绕开它。
版本管理与变更沟通
设计令牌是公共契约,变更会影响所有使用方。因此它需要像 API 一样管理版本。
变更类型 | 示例 | 版本影响 | 沟通方式 |
|---|---|---|---|
新增令牌 | 增加 | 次版本 | 发布说明 |
修改取值 |
| 补丁或次版本 | 视觉回归 + 说明 |
重命名 |
| 主版本 | 迁移指南 + 弃用期 |
删除令牌 | 移除废弃变量 | 主版本 | 提前公告 |
新增主题 | 增加高对比主题 | 次版本 | 文档更新 |
重命名和删除是最危险的,因为它们会直接让组件报错或回退到默认值。稳妥做法是先并行保留旧名一段时间,在构建时输出警告,给使用方迁移窗口。令牌的废弃日志,应该像依赖库的 changelog 一样被认真对待。
团队协作:谁来决定令牌
设计令牌不是设计师或前端单方面的工作,它需要明确的角色分工。常见分工如下:
设计系统负责人:决定令牌结构和命名规范。
视觉设计师:提供原始值和语义映射建议。
设计工程师:维护构建脚本、类型定义和文档。
组件维护者:按需提出组件级令牌申请。
产品团队:作为使用方反馈缺口,但不直接新增令牌。
新增令牌应该有门槛。不是每个“这个颜色和默认不一样”的需求都要变成令牌。可以先问三个问题:这个值是否会被复用?是否属于已有语义角色?是否能通过组合现有令牌解决?如果答案都是否,才考虑新增。令牌库越克制,越容易被信任。
落地检查清单
是否区分了原始值、语义令牌和组件令牌三层?
命名是否描述用途而非外观?
组件是否只引用语义层和组件层,不直接引用原始值?
亮色与暗色模式是否通过映射表切换?
多品牌场景是否只替换映射,不改组件?
令牌源文件是否单一,且能同步到设计工具与多端代码?
是否有类型定义,拼错令牌能否被工具发现?
变更是否有版本记录和迁移指南?
是否定期清理废弃令牌?
新成员能否在不问人的情况下找到正确令牌?
设计令牌的价值,不在于它让变量变多了,而在于它让决策变少了。设计师不必每次重新选色,开发不必猜测某个灰色是否合规,产品换主题时不必重做界面。它把“品牌语言”和“代码语言”对齐成同一套词汇,让设计系统从一份文档变成可执行的契约。做好这一步,组件库、暗色模式、多品牌和跨端一致性才有真正稳固的地基。
演示站内容均来自互联网,如有侵权,请与我联系
文章不错?点个赞呗~