设计系统搭建与设计 Token 管理体系实践
设计系统搭建与设计 Token 管理体系实践一、设计系统的本质从组件库到设计资产的系统性工程设计系统这个词在近年来的前端社区中被提及的频率越来越高但不同团队对它的理解却存在显著差异。有人认为设计系统就是一套共享的组件库有人认为它是设计规范的代码化也有人认为它是包含设计原则、模式库、组件库、工具链在内的完整生态系统。这些理解都有其合理性但从工程化的角度来看设计系统的本质是一套关于如何组织和管理设计资产的系统性方法论。一个成熟的设计系统需要解决的核心问题是什么当团队规模从几个人扩展到几十人甚至上百人时如何保证不同设计师和开发者产出的界面在视觉和交互上保持一致当产品需要支持多主题如暗色模式、多品牌如面向不同客户的白标版本时如何避免为每个变体维护独立的代码库当设计规范发生变更时如何快速同步到所有相关代码设计系统正是为解决这些问题而生的。本文将从工程化的角度探讨设计系统的搭建与 Token 管理体系的实践重点关注设计系统如何与前端工程流程深度融合以及如何通过 Token 体系实现设计资产的灵活复用和高效管理。二、Design Token 的语义化分层体系2.1 Token 分层的必要性Design Token设计令牌是设计系统中最重要的概念之一。它将设计决策如品牌主色是蓝色从具体的实现如 #1890FF 这个十六进制值中抽象出来形成一种语义化的中间层。这种抽象带来的灵活性是惊人的——当需要将品牌色从蓝色切换为绿色时只需要修改一处 Token 定义而不需要在数百个代码文件中进行替换。然而Token 的组织方式直接决定了设计系统的可维护性和可扩展性。过于扁平的 Token 结构会导致语义模糊如 color-blue-500 既可能表示用于背景的蓝色也可能表示用于文字的蓝色过于深层的嵌套又会导致使用上的不变如 component-button-primary-background-hover。工程化的最佳实践是将 Token 划分为语义清晰的几层。flowchart TD A[Global Tokensbr/全局基础值] -- B[Semantic Tokensbr/语义化 Tokens] B -- C[Component Tokensbr/组件级 Tokens] A -- A1[#1890FFbr/品牌蓝] A -- A2[#52C41Abr/成功绿] A -- A3[#FF4D4Fbr/警告红] B -- B1[color.primarybr/品牌蓝] B -- B2[color.successbr/成功绿] B -- B3[color.dangerbr/警告红] C -- C1[button.primary.backgroundbr/color.primary] C -- C2[button.primary.hoverbr/color.primary.darken10] C -- C3[link.default.colorbr/color.text] style A fill:#e1f5fe style B fill:#fff3e0 style C fill:#e8f5e92.2 三层 Token 体系的实现基于上述分层理念我们建立了一套三层 Token 体系Global 层存储所有设计决策的原始值Semantic 层基于 Global 层定义设计语义的对应关系Component 层将 Semantic 层与具体组件的属性绑定。// Global Tokens - 原始设计值 const globalTokens { colors: { // 蓝色系 blue: { 50: #E6F4FF, 100: #BAE0FF, 200: #91CAFF, 300: #69B1FF, 400: #4096FF, 500: #1890FF, // 品牌蓝基准 600: #096DD9, 700: #0050B3, 800: #003A8C, 900: #002466, }, // 绿色系 green: { 500: #52C41A, 600: #389E0D, // ... }, // 红色系 red: { 500: #FF4D4F, 600: #D4380D, // ... } }, spacing: { xs: 4px, sm: 8px, md: 16px, lg: 24px, xl: 32px, xxl: 48px, }, typography: { fontSize: { xs: 12px, sm: 14px, md: 16px, lg: 18px, xl: 20px, xxl: 24px, }, lineHeight: { tight: 1.25, normal: 1.5, relaxed: 1.75, } }, borderRadius: { sm: 2px, md: 4px, lg: 8px, full: 9999px, } };// Semantic Tokens - 语义化映射 const semanticTokens { colors: { // 品牌色 brand: { default: globalTokens.colors.blue[500], hover: globalTokens.colors.blue[600], active: globalTokens.colors.blue[700], disabled: globalTokens.colors.blue[300], }, // 功能色 success: { default: globalTokens.colors.green[500], background: globalTokens.colors.green[50], }, danger: { default: globalTokens.colors.red[500], background: globalTokens.colors.red[50], }, // 文字色 text: { primary: #000000E0, // rgba(0, 0, 0, 0.88) secondary: #00000073, // rgba(0, 0, 0, 0.45) disabled: #00000040, // rgba(0, 0, 0, 0.25) }, // 背景色 background: { primary: #FFFFFF, secondary: #FAFAFA, tertiary: #F5F5F5, } }, // 间距语义化 spacing: { componentPadding: globalTokens.spacing.md, sectionGap: globalTokens.spacing.lg, pageMargin: globalTokens.spacing.xl, } };// Component Tokens - 组件级 Token const componentTokens { button: { primary: { backgroundColor: semanticTokens.colors.brand.default, backgroundColorHover: semanticTokens.colors.brand.hover, backgroundColorActive: semanticTokens.colors.brand.active, backgroundColorDisabled: semanticTokens.colors.brand.disabled, textColor: #FFFFFF, padding: ${semanticTokens.spacing.spacing.sm} ${semanticTokens.spacing.spacing.md}, borderRadius: globalTokens.borderRadius.md, fontSize: globalTokens.typography.fontSize.md, }, secondary: { backgroundColor: transparent, borderColor: semanticTokens.colors.brand.default, textColor: semanticTokens.colors.brand.default, // ... } }, input: { default: { backgroundColor: semanticTokens.colors.background.primary, borderColor: #D9D9D9, borderColorHover: semanticTokens.colors.brand.default, borderColorFocus: semanticTokens.colors.brand.default, textColor: semanticTokens.colors.text.primary, placeholderColor: semanticTokens.colors.text.disabled, padding: ${semanticTokens.spacing.spacing.sm} ${semanticTokens.spacing.spacing.md}, borderRadius: globalTokens.borderRadius.md, } } };三、Token 转换与多平台输出3.1 Style Dictionary 的标准化工作流管理多平台设计系统的一个核心挑战是如何将设计团队维护的设计值如 Sketch/Figma 中的样式转换为各平台可用的代码。Style Dictionary 是一个来自 Amazon 的开源工具它提供了一套标准化的设计值转换工作流能够将一套设计定义输出为 iOSSwift、AndroidXML、WebCSS/SCSS/JavaScript等多平台的代码。// style-dictionary 配置文件 // config.json { source: [tokens/**/*.json], platforms: { web: { transformGroup: css, prefix: ds, buildPath: dist/css/, files: [{ destination: variables.css, format: css/variables, filter: { attributes: { category: color } } }] }, ios: { transformGroup: ios-swift, buildPath: dist/ios/, files: [{ destination: StyleDictionary.swift, format: ios-swift/class.swift, className: StyleDictionary }] }, android: { transformGroup: android, buildPath: dist/android/, files: [{ destination: styles.xml, format: android/resources }] } } }3.2 自定义 Transform 与 Format 实现Style Dictionary 提供了丰富的内置 Transform 和 Format但在实际项目中往往需要根据团队特定需求开发自定义的 Transform 和 Format。// 自定义 Transform颜色自动添加 alpha 变体 const registerTransforms () { StyleDictionary.registerTransform({ name: color/withAlpha, type: value, matcher: (token) { return token.attributes.category color token.name.includes(primary); }, transformer: (token) { const value hexToRgba(token.value, 0.8); return value; } }); }; // 自定义 Format生成 CSS Custom Properties 并附带 JavaScript 导出 const registerFormats () { StyleDictionary.registerFormat({ name: css/custom-properties-js, formatter: (dictionary, config) { const cssLines [:root {]; const jsExports {}; dictionary.allProperties.forEach(prop { const varName --${config.prefix}-${prop.name}; cssLines.push( ${varName}: ${prop.value};); jsExports[prop.name] var ${varName}; }); cssLines.push(}); return ${cssLines.join(\n)}\n\nconst tokens ${JSON.stringify(jsExports, null, 2)};; } }); };四、组件库与设计系统的协同治理4.1 组件 Token 覆盖机制在实际项目中组件往往需要支持多种变体和主题。传统的实现方式是为每种变体编写独立的样式代码这会导致代码膨胀和难以维护。通过组件 Token 覆盖机制我们可以用声明式的方式控制组件外观而不需要编写重复的样式代码。// 组件 Token 覆盖机制的实现 class ComponentTokenOverride { constructor(defaultTokens) { this.defaultTokens defaultTokens; this.overrides {}; } // 设置组件的 Token 覆盖值 setOverride(componentName, tokenPath, value) { if (!this.overrides[componentName]) { this.overrides[componentName] {}; } this.setNestedValue( this.overrides[componentName], tokenPath.split(.), value ); } // 获取组件的实际 Token 值合并后的 getComponentToken(componentName, tokenPath) { const defaultValue this.getNestedValue( this.defaultTokens, [componentName, ...tokenPath.split(.)] ); const overrideValue this.overrides[componentName] ? this.getNestedValue( this.overrides[componentName], tokenPath.split(.) ) : undefined; return overrideValue ! undefined ? overrideValue : defaultValue; } // 工具方法设置嵌套对象的值 setNestedValue(obj, path, value) { let current obj; for (let i 0; i path.length - 1; i) { if (!current[path[i]]) { current[path[i]] {}; } current current[path[i]]; } current[path[path.length - 1]] value; } // 工具方法获取嵌套对象的值 getNestedValue(obj, path) { return path.reduce((current, key) current?.[key], obj); } } // 使用示例 const tokenManager new ComponentTokenOverride(componentTokens); // 设置暗色主题的覆盖 tokenManager.setOverride(button, primary.backgroundColor, #1F1F1F); tokenManager.setOverride(button, primary.textColor, #FFFFFF); tokenManager.setOverride(input, default.backgroundColor, #1F1F1F);4.2 主题切换的工程实现多主题支持是现代前端应用的常见需求。通过 Token 体系主题切换可以优雅地实现为 Token 覆盖层的切换而不需要重新加载整个应用。// 主题管理器 class ThemeManager { constructor() { this.currentTheme light; this.themeOverrides { light: {}, dark: { colors: { background: { primary: #1F1F1F, secondary: #2D2D2D, }, text: { primary: #FFFFFFE0, secondary: #FFFFFFA6, } } }, brandBlue: { colors: { brand: { default: #0066CC, hover: #0052A3, } } } }; this.listeners []; } // 切换主题 switchTheme(themeName) { if (!this.themeOverrides[themeName]) { console.warn(Theme ${themeName} not found); return; } this.currentTheme themeName; this.applyTheme(); this.notifyListeners(); } // 应用主题到 DOM applyTheme() { const root document.documentElement; const override this.themeOverrides[this.currentTheme]; // 清除旧的主题变量 Object.keys(this.themeOverrides.dark).forEach(category { const categoryValues this.themeOverrides.dark[category]; if (typeof categoryValues object) { Object.keys(categoryValues).forEach(key { const varName --ds-${category}-${key}; root.style.removeProperty(varName); }); } }); // 应用新的主题变量 this.setCssVariables(root, override); } setCssVariables(element, obj, prefix ) { for (const [key, value] of Object.entries(obj)) { const varName --ds-${prefix}${key}; if (typeof value object) { this.setCssVariables(element, value, ${key}-); } else { element.style.setProperty(varName, value); } } } // 监听主题变化 addListener(listener) { this.listeners.push(listener); } notifyListeners() { this.listeners.forEach(listener listener(this.currentTheme) ); } }五、Trade-offs设计系统建设中的现实挑战5.1 灵活性与约束性的博弈设计系统的核心价值之一是为团队提供统一的设计决策减少重复决策带来的成本。然而过度约束的设计系统可能会抑制创新让产品变得千篇一律。如何在提供足够的约束和保留必要的灵活性之间取得平衡是设计系统建设中的永恒难题。解决这个问题的关键在于分层设计。底层的基础 Token 和组件应当是高度约束的确保一致性和可维护性而上层的组合模式和业务组件则应当保留足够的灵活空间允许不同业务线根据自身特点进行差异化定制。5.2 多团队协作的治理困境当设计系统需要服务于多个团队时治理结构的设计变得至关重要。谁有权力决定设计系统的演进方向如何处理不同团队之间的需求冲突如何确保设计系统的变更不会破坏已有的产品实现这些问题没有标准答案每个团队需要根据自身情况设计合适的治理机制。常见的模式包括设立专职的设计系统团队负责核心维护通过 RFCRequest for Comments流程让各团队参与决策建立设计系统委员会处理争议和变更审批。5.3 设计与开发的同步成本设计系统需要设计工具如 Figma和前端代码库之间的同步。当设计师在 Figma 中修改了某个组件样式这个变更需要被反映到代码中。传统上这个同步过程依赖人工操作容易出现不一致。近年来Design Token 的标准化和 Figma 插件生态的发展正在改善这一状况。通过 Figma 插件可以直接导出 Token 到代码仓库通过 CI/CD 流程可以自动验证代码与设计的一致性。但这些自动化方案仍然需要团队投入精力搭建和维护需要在效率提升和投入成本之间权衡。六、总结设计系统的建设是一项系统性工程Design Token 作为其核心概念为设计资产的组织和复用提供了语义化的分层体系。通过 Global、Semantic、Component 三层划分我们能够在保持设计一致性的同时为组件变体和主题切换提供足够的灵活性。Token 转换工具如 Style Dictionary的引入使得一套设计定义能够自动输出为多平台代码大大降低了多平台维护的成本。自定义的 Transform 和 Format 机制则为团队提供了根据特定需求扩展的能力。组件 Token 覆盖机制和主题切换的实现展示了如何将 Token 体系转化为工程实践中的具体能力。这些能力使得多主题、多变体的界面开发变得可控且高效。然而设计系统建设也面临着灵活性与约束性平衡、多团队治理、设计开发同步等现实挑战。这些挑战需要团队根据自身情况持续探索和迭代没有一劳永逸的解决方案。