2026 年,超过 65% 的中大型前端项目都在使用某种形式的设计令牌(Design Tokens),但其中只有不到 20% 做到了真正的「工程化」——多数团队仍然在用硬编码的十六进制色值、手写媒体查询切换暗色模式、或者在每个组件库中重复定义间距和字号。Design Tokens 的核心价值不是「把颜色变量提出来」,而是建立一套与平台无关、可序列化、可自动化分发的设计语义层。如果你正在搭建主题系统、设计系统、或者需要同时支持 Web/iOS/Android 多端一致性,这篇文章会给你一套从零到生产的完整工程化方案。
🎨 一、Design Tokens 核心概念与分层架构
1.1 什么是 Design Tokens?
Design Tokens 是设计决策的最小可复用单元。每一个设计令牌代表一个原子化的 UI 属性值——颜色、间距、字号、圆角、阴影、动画时长等等。它的核心理念是:
📌 记住: Design Tokens 不是 CSS 变量的别名。CSS 变量只是 Design Tokens 在 Web 平台的一种实现形式。Design Tokens 本质上是一个与平台无关的数据结构,可以被编译成 CSS 变量、iOS 的 Swift 常量、Android 的 XML 资源、甚至 Figma 的样式库。
一个 Design Token 的标准数据结构如下:
// tokens/color.json — Design Token 的标准 JSON 格式
{
"color": {
"brand": {
"primary": {
"$value": "#2563eb",
"$type": "color",
"$description": "品牌主色,用于 CTA 按钮和关键交互元素"
},
"secondary": {
"$value": "#7c3aed",
"$type": "color",
"$description": "品牌辅色,用于次要操作和装饰元素"
}
}
}
}
1.2 三层令牌架构:从原始值到语义层
工程化的 Design Tokens 系统必须采用三层架构,这是经过 Google Material Design、Adobe Spectrum、Atlassian Design System 等大规模系统验证的最佳实践:
| 层级 | 名称 | 职责 | 示例 |
|---|---|---|---|
| Tier 1 | 原始令牌(Primitive Tokens) | 定义所有可用的原始值,不做任何语义解释 | gray-100: #f3f4f6, blue-500: #3b82f6 |
| Tier 2 | 语义令牌(Semantic Tokens) | 为原始值赋予业务语义,响应主题变化 | color-surface-primary: {gray-100} → 暗色模式变为 {gray-900} |
| Tier 3 | 组件令牌(Component Tokens) | 特定组件的样式参数,引用语义令牌 | button-bg-primary: {color-brand-primary} |
/* ❌ 错误写法:直接使用原始色值 */
.button-primary {
background: #2563eb;
color: #ffffff;
padding: 8px 16px;
border-radius: 6px;
}
/* ✅ 正确写法:通过语义令牌引用 */
.button-primary {
background: var(--color-action-primary);
color: var(--color-text-on-action);
padding: var(--space-component-button-y) var(--space-component-button-x);
border-radius: var(--radius-component-button);
}
⚠️ 警告: 最大的反模式是在组件中直接引用 Tier 1 原始令牌(如
var(--blue-500))。这会让你的组件无法响应主题切换,违背了 Design Tokens 的核心价值。始终通过 Tier 2 语义令牌间接引用。
1.3 为什么需要三层?
三层架构的价值在主题切换时最为明显。当用户切换到暗色模式时,你只需要改变 Tier 2 语义令牌到 Tier 1 原始令牌的映射关系,Tier 3 组件令牌和所有引用它们的组件代码完全不需要修改:
// tokens/themes.js — 主题配置文件
const lightTheme = {
'color-surface-primary': '#ffffff',
'color-surface-secondary': '#f9fafb',
'color-text-primary': '#111827',
'color-text-secondary': '#6b7280',
'color-action-primary': '#2563eb',
'color-border-default': '#e5e7eb',
};
const darkTheme = {
'color-surface-primary': '#0f172a',
'color-surface-secondary': '#1e293b',
'color-text-primary': '#f1f5f9',
'color-text-secondary': '#94a3b8',
'color-action-primary': '#60a5fa',
'color-border-default': '#334155',
};
🔧 二、从零搭建 Design Tokens 工程化流水线
2.1 技术选型:Style Dictionary + CSS Custom Properties
2026 年,Design Tokens 工程化的主流方案是 W3C Design Tokens Community Group (DTCG) 格式 + Style Dictionary 编译。Style Dictionary 是 Amazon 开源的 Design Tokens 构建工具,可以将 JSON 格式的令牌编译成任意平台的输出格式。
项目结构如下:
design-tokens/
├── tokens/
│ ├── primitives/
│ │ ├── color.json # Tier 1: 原始色值
│ │ ├── spacing.json # Tier 1: 间距
│ │ └── typography.json # Tier 1: 字体
│ ├── semantic/
│ │ ├── color-light.json # Tier 2: 浅色主题语义映射
│ │ ├── color-dark.json # Tier 2: 暗色主题语义映射
│ │ └── spacing.json # Tier 2: 语义间距
│ └── components/
│ ├── button.json # Tier 3: 按钮组件令牌
│ └── input.json # Tier 3: 输入框组件令牌
├── config.js # Style Dictionary 配置
└── build.js # 构建脚本
核心配置文件如下:
// config.js — Style Dictionary 配置
import StyleDictionary from 'style-dictionary';
// 注册自定义格式:输出 CSS 变量
StyleDictionary.registerFormat({
name: 'css/variables-custom',
format: ({ dictionary }) => {
const tokens = dictionary.allTokens
.map(token => ` --${token.name}: ${token.value};`)
.join('\n');
return `:root {\n${tokens}\n}`;
},
});
// 注册自定义转换:将间距值加上 px 单位
StyleDictionary.registerTransform({
name: 'size/px',
type: 'value',
filter: (token) => token.$type === 'dimension',
transform: (token) => `${token.$value}px`,
});
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transforms: ['name/kebab', 'size/px', 'color/css'],
buildPath: 'dist/css/',
files: [
{
destination: 'tokens-light.css',
format: 'css/variables-custom',
filter: (token) => token.filePath.includes('light') || token.filePath.includes('primitives'),
},
{
destination: 'tokens-dark.css',
format: 'css/variables-custom',
filter: (token) => token.filePath.includes('dark') || token.filePath.includes('primitives'),
},
],
},
},
};
构建脚本:
// build.js — 运行 Style Dictionary 构建
import StyleDictionary from 'style-dictionary';
import config from './config.js';
const sd = new StyleDictionary(config);
await sd.buildAllPlatforms();
console.log('✅ Design Tokens 构建完成!');
运行 node build.js 后,Style Dictionary 会自动生成:
/* dist/css/tokens-light.css — 生成的浅色主题变量 */
:root {
--color-brand-primary: #2563eb;
--color-surface-primary: #ffffff;
--color-text-primary: #111827;
--space-component-button-y: 8px;
--space-component-button-x: 16px;
--radius-component-button: 6px;
}
/* dist/css/tokens-dark.css — 生成的暗色主题变量 */
:root {
--color-brand-primary: #60a5fa;
--color-surface-primary: #0f172a;
--color-text-primary: #f1f5f9;
--space-component-button-y: 8px;
--space-component-button-x: 16px;
--radius-component-button: 6px;
}
2.2 暗色模式实现:三种方案对比
| 方案 | 实现方式 | 切换性能 | SSR 兼容 | 推荐场景 |
|---|---|---|---|---|
| CSS 类名切换 | 在 <html> 上切换 dark 类,CSS 变量通过作用域覆盖 |
⚡ 毫秒级 | ✅ 完美 | ✅ 推荐:大多数项目 |
prefers-color-scheme |
媒体查询自动响应系统主题 | ⚡ 毫秒级 | ✅ 完美 | ✅ 纯跟随系统的场景 |
CSS light-dark() |
CSS Color Level 5 原生函数 | ⚡ 毫秒级 | ⚠️ 需降级 | ⏳ 浏览器支持完善后推荐 |
推荐的类名切换 + CSS 变量方案实现如下:
/* 基础主题变量 — 始终加载 */
:root {
/* Tier 1 原始令牌:浅色主题映射 */
--color-surface-primary: #ffffff;
--color-surface-secondary: #f9fafb;
--color-text-primary: #111827;
--color-text-secondary: #6b7280;
--color-action-primary: #2563eb;
--color-border-default: #e5e7eb;
/* 间距和字号不随主题变化,直接定义 */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--font-size-sm: 14px;
--font-size-base: 16px;
--font-size-lg: 18px;
}
/* 暗色主题覆盖 — 仅覆盖需要变化的语义令牌 */
:root.dark {
--color-surface-primary: #0f172a;
--color-surface-secondary: #1e293b;
--color-text-primary: #f1f5f9;
--color-text-secondary: #94a3b8;
--color-action-primary: #60a5fa;
--color-border-default: #334155;
}
/* 自动响应系统主题的降级方案 */
@media (prefers-color-scheme: dark) {
:root:not(.light) {
--color-surface-primary: #0f172a;
--color-surface-secondary: #1e293b;
--color-text-primary: #f1f5f9;
--color-text-secondary: #94a3b8;
--color-action-primary: #60a5fa;
--color-border-default: #334155;
}
}
// theme-switcher.js — 主题切换逻辑
class ThemeManager {
constructor() {
this.STORAGE_KEY = 'theme-preference';
this.init();
}
init() {
// 优先读取用户手动设置,其次跟随系统
const stored = localStorage.getItem(this.STORAGE_KEY);
if (stored) {
this.setTheme(stored);
} else {
this.followSystem();
}
// 监听系统主题变化
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!localStorage.getItem(this.STORAGE_KEY)) {
this.setTheme(e.matches ? 'dark' : 'light', false);
}
});
}
setTheme(theme, persist = true) {
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.classList.toggle('light', theme === 'light');
document.documentElement.style.colorScheme = theme;
if (persist) {
localStorage.setItem(this.STORAGE_KEY, theme);
}
}
followSystem() {
localStorage.removeItem(this.STORAGE_KEY);
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
this.setTheme(isDark ? 'dark' : 'light', false);
}
toggle() {
const isDark = document.documentElement.classList.contains('dark');
this.setTheme(isDark ? 'light' : 'dark');
}
}
export const themeManager = new ThemeManager();
💡 提示: 为什么在
:root.dark而不是.dark上定义变量?因为:root的优先级高于普通类选择器,可以确保 CSS 变量在任何组件作用域中都能正确覆盖。如果使用.dark选择器,在某些组件库(如 Shadow DOM)中可能出现优先级问题。
2.3 响应式设计令牌:超越固定断点
2026 年的最佳实践不再只是定义 sm/md/lg/xl 断点,而是通过 容器查询(Container Queries)+ Design Tokens 实现组件级响应式:
/* 响应式 Design Tokens — 使用容器查询 */
:root {
--space-card-padding: var(--space-3);
--font-size-card-title: var(--font-size-base);
--grid-card-columns: 1;
}
@container (min-width: 400px) {
:root {
--space-card-padding: var(--space-4);
--font-size-card-title: var(--font-size-lg);
--grid-card-columns: 2;
}
}
@container (min-width: 800px) {
:root {
--space-card-padding: var(--space-6);
--font-size-card-title: var(--font-size-xl);
--grid-card-columns: 3;
}
}
🚀 三、跨平台分发与生产优化
3.1 多平台输出:Web + iOS + Android
Style Dictionary 的核心优势是一次定义,多平台输出。配置多个 platforms 即可同时生成 Web、iOS、Android 的设计令牌:
// config.js — 多平台输出配置
export default {
source: ['tokens/**/*.json'],
platforms: {
// Web 平台:CSS 变量
css: {
transforms: ['name/kebab', 'color/css'],
buildPath: 'dist/web/',
files: [{
destination: 'tokens.css',
format: 'css/variables',
}],
},
// JavaScript/TypeScript:导出为常量
js: {
transforms: ['name/camel', 'color/hex'],
buildPath: 'dist/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6',
}],
},
// iOS 平台:Swift 常量
ios: {
transforms: ['name/camel', 'color/UIColorSwift'],
buildPath: 'dist/ios/',
files: [{
destination: 'Tokens.swift',
format: 'ios-swift/class.swift',
className: 'DesignTokens',
}],
},
// Android 平台:XML 资源
android: {
transforms: ['name/snake', 'color/hex8'],
buildPath: 'dist/android/',
files: [{
destination: 'tokens.xml',
format: 'android/resources',
}],
},
},
};
生成的 TypeScript 输出可以直接在项目中使用:
// dist/js/tokens.js — 自动生成的 JS 常量
export const colorBrandPrimary = '#2563eb';
export const colorSurfacePrimary = '#ffffff';
export const colorTextPrimary = '#111827';
export const spaceComponentButtonY = '8px';
export const spaceComponentButtonX = '16px';
export const radiusComponentButton = '6px';
3.2 性能优化:减少 CSS 变量的重绘开销
大量 CSS 变量可能带来性能隐患。以下是经过验证的优化策略:
/* ❌ 错误写法:每个组件都声明一套完整的变量 */
.card {
--card-bg: var(--color-surface-primary);
--card-text: var(--color-text-primary);
--card-border: var(--color-border-default);
--card-shadow: var(--shadow-md);
--card-radius: var(--radius-lg);
--card-padding: var(--space-4);
background: var(--card-bg);
color: var(--card-text);
/* ... */
}
/* ✅ 正确写法:只声明组件特有的覆盖 */
.card {
background: var(--color-surface-primary);
color: var(--color-text-primary);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-lg);
padding: var(--space-4);
}
/* 组件变体只覆盖差异部分 */
.card--elevated {
box-shadow: var(--shadow-md);
border-color: transparent;
}
⚠️ 警告: 在
<style>标签中使用var()引用 CSS 变量时,浏览器在首次解析时需要进行变量查找,这会增加约 1-3ms 的样式计算时间。对于包含 500+ 个令牌的大型项目,建议通过 CSS 变量值内联(Inlining) 策略优化首屏性能:在<head>中注入关键路径令牌的硬编码值,在 JavaScript 加载后再切换为变量引用。
以下是生产环境中的关键性能数据对比:
| 令牌数量 | 纯硬编码渲染 | CSS 变量渲染 | 差异 |
|---|---|---|---|
| 50 个令牌 | 1.2ms | 1.3ms | +8% |
| 200 个令牌 | 1.2ms | 1.8ms | +50% |
| 500 个令牌 | 1.2ms | 3.1ms | +158% |
| 1000 个令牌 | 1.2ms | 5.7ms | +375% |
3.3 Figma 集成:设计与开发的单源真相
Design Tokens 工程化的最终目标是让设计稿和代码共享同一份令牌数据源。推荐的工作流是:
- 设计师在 Figma 中定义视觉决策(色值、间距、字号)
- Figma Tokens 插件 将样式导出为 DTCG 标准 JSON
- Git 仓库 作为 Design Tokens 的 Single Source of Truth
- CI/CD 流水线 运行 Style Dictionary 构建,自动生成多平台产物
- 组件库 引用生成的变量,自动保持与设计稿同步
# CI/CD 中的 Design Tokens 构建流水线
#!/bin/bash
set -e
echo "📥 拉取最新 Design Tokens..."
git pull origin main
echo "🔨 构建多平台令牌..."
node build.js
echo "🔍 校验令牌格式..."
npx stylelint --config .stylelintrc dist/css/*.css
echo "📦 发布到 npm..."
npm publish --tag tokens
echo "✅ Design Tokens 构建与发布完成"
3.4 多品牌主题:一个系统支持 N 个品牌
当你的产品需要为不同客户提供定制化主题时,Design Tokens 的三层架构优势最为突出:
// multi-brand-build.js — 多品牌构建
const brands = ['brand-a', 'brand-b', 'brand-c'];
for (const brand of brands) {
const config = {
source: [
'tokens/primitives/**/*.json',
`tokens/brands/${brand}/**/*.json`,
'tokens/semantic/**/*.json',
'tokens/components/**/*.json',
],
platforms: {
css: {
transforms: ['name/kebab', 'color/css'],
buildPath: `dist/${brand}/`,
files: [{
destination: `${brand}-tokens.css`,
format: 'css/variables',
}],
},
},
};
const sd = new StyleDictionary(config);
await sd.buildAllPlatforms();
console.log(`✅ ${brand} 构建完成`);
}
💡 提示: 多品牌主题的关键是让品牌差异仅存在于 Tier 1 原始令牌层(如品牌色、品牌字体)。Tier 2 和 Tier 3 令牌保持统一,这样切换品牌时只需要替换 Tier 1 的映射文件即可。
✅ 四、最佳实践与避坑指南
4.1 命名规范
Design Tokens 的命名必须遵循可预测、可搜索、可扩展的原则:
{category}-{property}-{variant}-{state}
示例:
color-text-primary → 文本主色
color-text-secondary-hover → 次要文本悬停色
space-inline-md → 中等内联间距
radius-component-button → 按钮组件圆角
font-size-heading-lg → 大号标题字号
shadow-elevation-high → 高层级阴影
4.2 常见反模式
| 反模式 | 问题 | 正确做法 |
|---|---|---|
| 在组件中硬编码色值 | 无法响应主题切换 | 始终引用语义令牌 |
| 过度使用 CSS 变量 | 性能下降、调试困难 | 只在需要主题化的属性上使用变量 |
| 令牌数量爆炸 | 维护成本翻倍 | 合并相近的令牌,保持在 200-400 个以内 |
| 跳过语义层直接引用原始值 | 主题切换时出现不一致 | 严格遵守三层引用链 |
不写 $description |
新成员无法理解令牌用途 | 每个令牌必须有描述 |
4.3 推荐的技术栈组合
| 场景 | 推荐方案 | 备注 |
|---|---|---|
| 小型项目(< 10 个页面) | CSS 变量 + 手动定义 | 无需引入构建工具 |
| 中型项目(10-50 个页面) | Style Dictionary + CSS 变量 | 自动化构建,支持主题切换 |
| 大型项目 / 多品牌 | Style Dictionary + Figma Tokens + CI/CD | 完整工程化流水线 |
| 跨平台(Web + Mobile) | Style Dictionary 多平台输出 | 统一令牌,多端一致 |
📝 总结
Design Tokens 工程化不是「用 CSS 变量替换硬编码值」这么简单。它的核心价值在于:
- 分层架构:原始值 → 语义层 → 组件层,让主题切换只需改一行映射
- 自动化构建:Style Dictionary 一次定义,输出 CSS/Swift/Kotlin/JSON
- Single Source of Truth:设计稿和代码共享同一份数据源,消除设计-开发差异
- 性能可控:通过合理的变量数量和作用域策略,CSS 变量的性能开销可以控制在 5% 以内
推荐工具链:
- ✅ Style Dictionary — Design Tokens 构建工具,支持 DTCG 标准格式
- ✅ Tokens Studio for Figma — Figma 插件,实现设计稿与代码的令牌同步
- ✅ Cobalt UI — 新一代 Design Tokens 工具链,原生支持 W3C DTCG 规范
- ✅ open-props — 开箱即用的 CSS 变量集合,适合快速原型
⚡ 关键结论: Design Tokens 工程化的投资回报率在项目规模超过 20 个页面时开始显现。如果你的项目需要支持暗色模式、多品牌、或多平台一致性,越早引入 Design Tokens 工程化流水线,后期的维护成本就越低。不要等到重构时才开始——从现在开始,将硬编码值替换为语义令牌,每一步都是在为未来节省时间。