设计 token:把颜色、间距、字号写成变量

同一个蓝色在代码里出现了十一次,每次改版都要全局搜索替换,还总漏掉一处。设计 token 不是设计团队的专属工程,而是一个人让界面保持一致的最低成本做法:把颜色、间距、字号写成变量,分清原始值与语义,再控制在三十个以内。

写死在代码里的颜色,第二次改版就会背叛你。

一、同一个蓝色,十一种写法

以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。

周叙的看板工具写了两年。某次要统一主色,他全局搜索 #2F6FEB,找到十一次,分布在按钮、链接、选中态、图表和邮件模板里。改完之后,他又发现三处写的是 #2f6feb(小写),两处写的是 rgb(47,111,235),还有一处是设计稿里导出时偏了 3 个色阶的 #3070EC。

同一次改版,他花了一个下午做替换,仍然漏掉了邮件模板里的一处。用户收到的通知邮件里,按钮还是旧蓝色。

这不是粗心,而是缺少一层中间层。颜色、间距、字号直接写在组件里,每改一次就要全量搜索;搜索又依赖写法一致,而写法从来不会一致。

更麻烦的是,这个问题会随时间恶化。每加一个页面,就多几处硬编码;每接一个第三方组件,就多一套无法控制的颜色。两年后,同一份代码里会有三四套并行的视觉规则,谁也说不清哪一套是「对的」。

二、三层 token:原始值、语义、组件

设计 token 的本质是给每个视觉决定起一个名字,让组件引用名字而不是数值。

层级例子谁在用特点
原始值blue-500: #2F6FEB语义层只描述颜色本身,不含用途
语义color-action: blue-500组件、页面描述用途,换主题只改这一层
组件button-bg: color-action单个组件允许组件有局部差异

三层不是必须全建。一个人的产品,原始值 + 语义层就够用;组件层只给少数需要独立演化的组件(按钮、输入框、卡片)留口子。

语义命名要按用途,不按颜色。 color-action、color-danger、color-surface 是用途;color-blue、color-red 是颜色。按颜色命名,换主色时语义层就失去了意义。

同义词不要建两个。 color-primary 与 color-action 若指向同一个值,早晚会有一个被改掉。一个用途一个名字,宁可名字长一点。

组件层不是逃生通道。 组件 token 只能引用语义 token,不能直接写原始值。否则三层结构退化成两层,问题原样回来。

原始值层要有刻度感。 blue-500 比 blue-main 更容易扩展,因为它在暗示「还有 400 和 600」。一个人维护时不必凑满整套刻度,但命名方式要留出位置。

语义层是唯一允许被页面直接引用的层。 页面里出现 blue-500,就说明有人绕过了中间层。这条规则可以用脚本检查,比靠自觉可靠。

三、间距基数与字号阶梯

间距和字号是两套独立的刻度,各自需要一个基数。

间距基数:4
4, 8, 12, 16, 24, 32, 48, 64

字号阶梯:基数 16,比例 1.25
13, 16, 20, 25, 31, 39

行高:正文 1.5–1.6,标题 1.2–1.3
圆角:4(小)、8(中)、12(大)

基数选 4 还是 8,取决于界面密度。信息密集的后台适合 4,阅读型产品用 8 更舒展。关键不是选哪个,而是只选一个,并且所有间距都从它派生。

字号阶梯不要超过六档。 每多一档,就要多定义一次行高、多检查一次移动端适配。六档足够覆盖标题、正文、说明、辅助四类用途。

行高要跟着字号走。 小字号需要更大的相对行高(1.6),大标题可以收紧到 1.2。固定行高会让大标题散架、正文拥挤。

圆角也要进 token。 圆角是最容易被随手写死的属性,也是视觉一致性最直观的部分。三个档位够用,多了就是设计稿在替你决定。

图标尺寸也应纳入系统。 16、20、24 三个档位足够覆盖按钮内、导航栏与空状态。图标尺寸若随手写,视觉上会比颜色更早暴露出「拼装感」。

间距不要用「感觉差不多」的数。 13px、18px、22px 这类值一旦出现,就说明有人绕过了系统。它们不会立刻出问题,但会在半年后让界面看起来「说不出哪里不对」。

字号阶梯要跟着用途命名,不跟着数字命名。 text-body、text-caption、text-title 比 text-14、text-12 更耐用,因为后者会在你换基数时全部变成谎言。

行高与字号成对定义。 只给字号不给行高,每个使用处都要重新决定一次,最后的结果一定是同一个字号有四种行高。

四、一个人维护得住的规模

设计系统的失败很少因为做得太少,多数因为做得太多。

一个单人产品的 token 预算

颜色     ≤ 11   (主色 1 + 中性 7 + 语义 3)
间距     ≤ 8
字号     ≤ 6
行高     ≤ 3
圆角     ≤ 3
阴影     ≤ 2
合计     ≈ 30

超过 40 个,通常意味着有人在为个别页面开特例。特例本身不是错,但要显式记录:它属于哪个页面、为什么不能复用现有 token、什么时候回收。

先迁移,再扩展。 已有产品引入 token,第一步是把现有硬编码值归并成最少的集合,而不是先设计一套理想的刻度。归并后你会发现,47 个色值其实只有 9 个是不同的。

迁移按「改动频率 × 影响面」排序。 先改颜色(改版最频繁),再改间距(影响布局),最后改字号(影响阅读节奏)。一次只迁一类,每类迁完截图对比,确认没有视觉变化。

token 文件要只有一份来源。 无论用 CSS 变量、JSON 还是框架配置,最终要有一处是权威的。设计稿里的值若与代码不一致,以代码为准,并反过来修正设计稿。

给 token 写一句注释。 每个语义 token 后面写清「用在哪、不能用在哪」。半年后你自己也会忘记 color-muted 和 color-subtle 的区别。

特例要集中,不要散落。 某个图表需要第四种蓝色时,把它写成该图表组件内部的常量并注明原因,不要新增一个全局 chart-blue。局部特例一旦进入全局层,就会被别处误用。

不要为了「以后可能换主题」提前抽象。 深色模式、多品牌、白标都是真实需求,但在没有第二个消费方之前,三层结构会变成负担。先做两层,需要时再加。

迁移要留可回退的一步。 先在一两个页面替换 token,观察一周;确认没有视觉回归后,再推广到全部页面。一次性全量替换,出问题时你分不清是 token 定义错了,还是某处替换漏了。

五、行动清单与复算验收

  • 统计当前代码里出现的颜色、间距、字号、圆角的不同取值数
  • 把颜色归并到 11 个以内,间距归并到 8 个以内
  • 建立原始值层,命名按「色相 + 明度」或数字刻度
  • 建立语义层,命名按用途(action、surface、danger、muted)
  • 把组件里的硬编码值替换为语义 token,一次只替换一类
  • 每类替换后截图对比,确认没有非预期视觉变化
  • 统计 token 总数,超过 40 个时逐个说明理由
  • 在 token 文件里为每个语义项写一句使用边界注释

验收标准:能报出 token 总数,以及改一次主色需要修改几处。 改主色仍需全局搜索的,说明还没建立语义层。

使用边界与实操验收

本文讲 token 的结构与规模,不涉及具体框架怎么实现,那属于前端工程化专题:从 Vite 到 WebAssembly 的全栈实践 ;色板与对比度属于第 03 篇;间距如何影响首屏布局属于第 05 篇;上线前的检查项属于第 09 篇。

本文也不主张「所有项目都该有设计系统」。只有一两个页面的落地页,直接把值写在样式里更划算。需要 token 的信号是:同一个值出现了三次以上,且你已经开始漏改。

验证以改动成本为准:记录一次主色变更实际花的时间。第一次可能是两小时,第二次应当降到十分钟以内。降不下来,说明还有组件在绕过 token。

另一个可复算的指标是「孤儿值」数量:用脚本扫描样式文件,统计不在 token 表里的颜色与间距值。目标不是零(内联样式与第三方组件总会有),而是逐月下降。

验收的最后一步是改一次试试。把主色换成另一个色相,记录改动点数量与耗时;如果超过三处、超过半小时,说明语义层还没有真正成为唯一入口。

关键参考与核验范围

本文的刻度与预算为教学演示。核验日期:2026-10-24。

上述两条只用于说明公开实践,不构成对任何设计系统或框架的推荐。token 的具体命名与层级可按项目规模调整,本文的 30 个预算不是行业标准。

系列导航

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Indie」更多文章

  1. 开源项目验收:十项能力、四维评分与三种不该开源的项目
  2. 治理、fork 与退出:17 位贡献者、82% 的提交与一次改协议的代价
  3. 双许可与企业版:九家询价、两家成交与 4,800 美元