治愈系设计系统的色彩体系:从直觉选色到 Token 化工程实践 治愈系设计系统的色彩体系从直觉选色到 Token 化工程实践一、色彩设计在开发流程中的脱节问题设计师在 Figma 中定义了一套柔和的暖色调色板主色是奶油杏色辅色是雾蓝和鼠尾草绿。开发拿到设计稿后在代码里写了color: #F5E6D3。两周后产品要求把杏色调亮 5%这个改动发生在 27 个文件中。更糟的是在深色模式下同一套色值完全不可用——雾蓝在深色背景上对比度不足符合 WCAG AA 标准的文字变成了浅灰。这种脱节的根源在于设计师的色板和开发的变量之间没有建立规范的映射关系。一个可维护的色彩体系需要三样东西语义化的 Token 命名、支持亮暗双模的色值映射、以及约束色值使用范围的 Lint 规则。通过在某产品的迭代中引入 Token 化色彩体系后全站改色的工时从 3 天降至 30 分钟暗色模式的覆盖率从 40% 提升到 95%。二、三层 Token 架构的映射机制色彩 Token 分为三个层次由抽象到具体让设计意图和工程实现解耦graph TB subgraph 第一层原始色板 P[Primitivesbr/基础色值] end subgraph 第二层语义Token S1[color-bg-primary] S2[color-text-primary] S3[color-border-default] S4[color-accent-soft] end subgraph 第三层组件Token C1[button-bg-hover] C2[card-border] C3[input-focus-ring] C4[badge-text] end P --|亮色模式映射| S1 P --|暗色模式映射| S1 S1 -- C1 S1 -- C2 S2 -- C4 S4 -- C1 S3 -- C2 S3 -- C3第一层 Primitives 是纯粹的颜色值如gray-100: #F7F7F7不与任何语义绑定。第二层语义 Token 表达颜色的用途而非颜色本身同一 Token 在不同主题下映射到不同的 Primitives。第三层组件 Token 直接给 Button、Card 等组件使用是开发者的唯一消费入口。三、响应暗色模式的 Token 生成器实现// 原始色板定义——不包含任何语义 const primitives { // 暖灰系——治愈系基调 warm: { 50: #FDF8F3, 100: #F5EDE3, 200: #E8D9C8, 300: #D4C4B0, 800: #5C4F3F, 900: #3D3228, }, // 雾蓝系——信息层级 blue: { 100: #E8EDF5, 500: #7B8FAD, 700: #4A5A73, }, // 鼠尾草绿系——正向反馈 green: { 100: #E8F0E8, 500: #7B9E7B, 700: #4B6B4B, }, } as const; // 语义 Token 配置——分离亮色/暗色的映射关系 interface SemanticTokens { bg: { primary: string; secondary: string; }; text: { primary: string; secondary: string; muted: string; }; accent: { soft: string; }; } // 亮色主题浅色背景 深色文字 const lightTokens: SemanticTokens { bg: { primary: primitives.warm[50], secondary: primitives.warm[100], }, text: { primary: primitives.warm[900], secondary: primitives.warm[800], muted: primitives.blue[500], }, accent: { soft: primitives.green[100], }, }; // 暗色主题深色背景 浅色文字同时降低对比度避免刺眼 const darkTokens: SemanticTokens { bg: { primary: primitives.warm[900], secondary: primitives.warm[800], }, text: { primary: primitives.warm[50], // 暗色下文字适当降低亮度避免发光感 secondary: primitives.warm[200], muted: primitives.blue[500], }, accent: { soft: primitives.green[700], }, }; // 组件 Token——开发者的唯一消费层 // 设计意图屏蔽原始色板和语义 Token 的细节 function buildComponentTokens(semantic: SemanticTokens) { return { button: { bg: semantic.accent.soft, text: semantic.text.primary, // 悬停态在 accent 基础上层叠半透明遮罩 hoverBg: ${semantic.accent.soft}CC, }, card: { bg: semantic.bg.secondary, // 卡片边框使用背景色加深而非独立边框色 border: ${semantic.bg.primary}99, text: semantic.text.primary, }, input: { bg: semantic.bg.secondary, text: semantic.text.primary, placeholder: semantic.text.muted, // 聚焦环使用 text 色的低不透明度版本 focusRing: ${semantic.text.muted}40, }, }; } // 运行时根据系统主题偏好自动切换 function getComponentTokens(prefersDark: boolean) { const semantic prefersDark ? darkTokens : lightTokens; return buildComponentTokens(semantic); }三个关键设计考量as const确保色值不可变防止运行时意外修改悬停态使用半透明叠加而非独立色值减少 Token 数量聚焦环复用现有 Token 而非新增降低维护负担。四、色彩体系的工程约束与自动化Token 的数量会随着设计迭代不断增长。当团队中有人新增一个color-warning-bright而其他人不知道时色值的碎片化就开始了。可以采用 Stylelint 插件在 CI 中拦截裸色值// .stylelintrc.json 中的色彩约束规则 { rules: { color-no-hex: true, color-named: never, function-disallowed-list: [rgb, rgba, hsl, hsla] } }但这套规则也有代价调试阶段想临时试一个颜色会收到 Lint 报错开发体验下降。折中方案是在开发环境开放裸色值使用在 CI 构建阶段强制检查用 Git Hook 在两阶段之间做平衡。色彩体系的可访问性底线。治愈系设计常用低饱和度配色很容易出现对比度不足。在语义 Token 定义阶段就应该用自动化工具校验 WCAG AA 标准而非等 QA 提 Bug 再修复。五、总结色彩 Token 化的核心收益设计稿改动映射到 Token 层代码零变更暗色模式由语义 Token 的映射表自动推导无需重写组件Lint 规则在 CI 阶段拦截裸色值防止 Token 碎片化。落地步骤提取设计稿中所有色值到 Primitives 层定义语义 Token显式声明亮色和暗色两套映射基于语义 Token 构建组件 Token发布为可消费的设计 Token 包集成 Stylelint 规则CI 中拦截裸色值使用。