当前位置: 首页 > news >正文

设计 Token 系统建设:从颜色变量到设计决策的工程化体系

设计 Token 系统建设:从颜色变量到设计决策的工程化体系

一、Token 不是变量:从样式复用到设计决策的抽象

设计 Token 常被误解为"CSS 变量的另一种写法"。但 Token 的本质是设计决策的抽象——--color-primary不是"蓝色",而是"品牌主色",蓝色只是当前的决策值。当品牌升级时,只需修改 Token 的值,所有引用该 Token 的组件自动更新。这种抽象层级比 CSS 变量更高——CSS 变量是技术实现,Token 是设计语义。

Token 系统的层级结构:全局 Token(Global Token)定义原始值(如--gray-900: #1a1a2e),别名 Token(Alias Token)定义语义(如--color-text-primary: var(--gray-900)),组件 Token(Component Token)定义组件级样式(如--button-bg: var(--color-primary))。三级结构确保了修改的影响范围可控。

二、Token 系统架构:三级抽象与主题切换

Token 系统的核心是三级抽象:Global → Alias → Component。Global Token 是最底层的原始值,不包含语义;Alias Token 赋予语义,是设计系统的核心;Component Token 将 Alias Token 绑定到具体组件,实现组件级定制。

flowchart TB subgraph Global Token A1[--gray-900: #1a1a2e] A2[--blue-500: #3b82f6] A3[--spacing-4: 16px] A4[--radius-md: 8px] end subgraph Alias Token B1[--color-text-primary: var(--gray-900)] B2[--color-primary: var(--blue-500)] B3[--spacing-md: var(--spacing-4)] B4[--radius-default: var(--radius-md)] end subgraph Component Token C1[--button-bg: var(--color-primary)] C2[--button-padding: var(--spacing-md)] C3[--button-radius: var(--radius-default)] C4[--button-text: var(--color-text-on-primary)] end A1 --> B1 A2 --> B2 A3 --> B3 A4 --> B4 B2 --> C1 B3 --> C2 B4 --> C3 B1 --> C4 subgraph 主题切换 D[Light Theme] E[Dark Theme] end D --> B1 E --> B1

主题切换的实现原理:不同主题覆盖 Alias Token 的值。Light 主题下--color-bg指向--white,Dark 主题下指向--gray-900。Global Token 不变,Alias Token 随主题切换,Component Token 自动跟随。

三、生产级代码实现:Token 定义、主题系统与组件绑定

3.1 Token 定义文件

/* ======================================== Global Token: 原始值,不包含语义 为什么分离 Global 和 Alias:Global Token 是设计系统的"调色板",修改它影响全局; Alias Token 是"语义层",修改它只影响 特定语义场景。分离后修改更安全 ======================================== */ :root { /* 颜色 - 灰度 */ --gray-50: #f8f9fa; --gray-100: #f1f3f5; --gray-200: #e9ecef; --gray-300: #dee2e6; --gray-400: #ced4da; --gray-500: #adb5bd; --gray-600: #868e96; --gray-700: #495057; --gray-800: #343a40; --gray-900: #212529; /* 颜色 - 品牌色 */ --blue-50: #e7f5ff; --blue-100: #d0ebff; --blue-200: #a5d8ff; --blue-300: #74c0fc; --blue-400: #4dabf7; --blue-500: #339af0; --blue-600: #228be6; --blue-700: #1c7ed6; --blue-800: #1971c2; --blue-900: #1864ab; /* 间距 */ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px; /* 圆角 */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* 阴影 */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); /* 字体 */ --font-sans: "Inter", system-ui, -apple-system, sans-serif; --font-mono: "JetBrains Mono", "Fira Code", monospace; /* 字号 */ --text-xs: 0.75rem; --text-sm: 0.875rem; --text-base: 1rem; --text-lg: 1.125rem; --text-xl: 1.25rem; --text-2xl: 1.5rem; /* 行高 */ --leading-tight: 1.25; --leading-normal: 1.5; --leading-relaxed: 1.75; }

3.2 Alias Token 与主题

/* ======================================== Alias Token: 语义层,主题切换的锚点 ======================================== */ /* Light 主题(默认) */ :root, [data-theme="light"] { /* 语义色 */ --color-bg: var(--gray-50); --color-bg-elevated: var(--white); --color-bg-sunken: var(--gray-100); --color-text-primary: var(--gray-900); --color-text-secondary: var(--gray-600); --color-text-tertiary: var(--gray-400); --color-text-on-primary: var(--white); --color-primary: var(--blue-600); --color-primary-hover: var(--blue-700); --color-primary-active: var(--blue-800); --color-primary-subtle: var(--blue-50); --color-border: var(--gray-300); --color-border-focus: var(--blue-500); /* 语义间距 */ --spacing-inline-xs: var(--space-1); --spacing-inline-sm: var(--space-2); --spacing-inline-md: var(--space-4); --spacing-inline-lg: var(--space-6); --spacing-stack-xs: var(--space-1); --spacing-stack-sm: var(--space-2); --spacing-stack-md: var(--space-4); --spacing-stack-lg: var(--space-6); } /* Dark 主题 */ /* 为什么 Dark 主题只覆盖 Alias Token: Global Token 是原始值,跨主题不变; 只覆盖 Alias Token 确保修改范围可控, 且组件 Token 自动跟随 Alias Token 变化 */ [data-theme="dark"] { --color-bg: var(--gray-900); --color-bg-elevated: var(--gray-800); --color-bg-sunken: var(--gray-950); --color-text-primary: var(--gray-50); --color-text-secondary: var(--gray-400); --color-text-tertiary: var(--gray-600); --color-text-on-primary: var(--gray-900); --color-primary: var(--blue-400); --color-primary-hover: var(--blue-300); --color-primary-active: var(--blue-200); --color-primary-subtle: var(--blue-900); --color-border: var(--gray-700); --color-border-focus: var(--blue-400); }

3.3 Component Token 与组件实现

/* ======================================== Component Token: 组件级样式绑定 ======================================== */ /* Button 组件 Token */ .button { /* 组件 Token 定义 */ --button-bg: var(--color-primary); --button-bg-hover: var(--color-primary-hover); --button-bg-active: var(--color-primary-active); --button-text: var(--color-text-on-primary); --button-padding-x: var(--spacing-inline-md); --button-padding-y: var(--spacing-stack-sm); --button-radius: var(--radius-md); --button-font: var(--font-sans); --button-font-size: var(--text-sm); --button-font-weight: 500; /* 使用 Component Token */ background: var(--button-bg); color: var(--button-text); padding: var(--button-padding-y) var(--button-padding-x); border-radius: var(--button-radius); font-family: var(--button-font); font-size: var(--button-font-size); font-weight: var(--button-font-weight); border: none; cursor: pointer; transition: background 0.15s ease; } .button:hover { background: var(--button-bg-hover); } .button:active { background: var(--button-bg-active); } /* 为什么用 Component Token 而非直接用 Alias Token: 直接用 Alias Token 时,修改 Alias Token 会 影响所有引用它的组件;Component Token 允许 单个组件覆盖样式而不影响其他组件 */ .button--secondary { --button-bg: transparent; --button-bg-hover: var(--color-primary-subtle); --button-text: var(--color-primary); --button-border: var(--color-primary); }

3.4 Token 管理工具

// Token 管理器:校验、转换和同步 class DesignTokenManager { constructor(tokens) { this.tokens = tokens; } // 校验 Token 的完整性 validate() { const errors = []; // 检查所有 Alias Token 是否引用了存在的 Global Token // 为什么需要校验:Token 引用链断裂会导致 // 样式失效,且难以排查(CSS 不会报错, // 只是回退到默认值) for (const [name, value] of Object.entries(this.tokens.alias)) { if (typeof value === "string" && value.startsWith("var(")) { const ref = value.match(/var\(([^,)]+)\)/)?.[1]; if (ref && !this.tokens.global[ref] && !this.tokens.alias[ref]) { errors.push(`Alias Token "${name}" 引用了不存在的 Token "${ref}"`); } } } return errors; } // 生成 CSS 变量声明 toCSS(theme = "light") { const lines = []; lines.push(":root,"); // Global Token for (const [name, value] of Object.entries(this.tokens.global)) { lines.push(` ${name}: ${value};`); } // Alias Token(按主题) lines.push(`[data-theme="${theme}"] {`); for (const [name, value] of Object.entries(this.tokens.alias[theme])) { lines.push(` ${name}: ${value};`); } lines.push("}"); return lines.join("\n"); } // 生成 Tailwind 配置 toTailwindConfig() { // 为什么支持 Tailwind 输出:Tailwind 的 // 配置与 Token 系统对齐,避免两套体系 return { colors: this._extractColors(), spacing: this._extractSpacing(), borderRadius: this._extractRadius(), fontSize: this._extractFontSizes(), }; } }

四、Token 系统的架构权衡:粒度、命名与同步成本

Token 粒度的权衡:Token 越细,定制能力越强,但维护成本越高。一个 Button 组件可以有 20 个 Component Token(背景、文字、边框、阴影、圆角、内边距……),也可以只有 3 个(variant、size、state)。建议核心组件(Button、Input、Card)用细粒度 Token,辅助组件用粗粒度 Token。

命名规范的一致性:Token 命名必须遵循统一的规则,否则团队无法快速理解 Token 的含义。推荐格式:{类别}-{属性}-{变体}-{状态},如--color-primary-hover。避免使用具体颜色名(如--blue-500)作为 Alias Token。

设计工具与代码的同步:Figma 中的样式变量和代码中的 Token 需要保持同步。手动同步容易遗漏,建议使用 Style Dictionary 或 Tokens Studio 等工具自动转换。同步是 Token 系统最大的运维成本。

多平台 Token 的统一:Web(CSS 变量)、iOS(Swift 颜色/间距常量)、Android(XML 资源)的 Token 格式不同。Style Dictionary 可以从统一的 JSON 源文件生成各平台的 Token 文件,是跨平台 Token 管理的标准方案。

五、总结

设计 Token 系统的核心是三级抽象:Global Token 定义原始值,Alias Token 赋予语义,Component Token 绑定组件。主题切换通过覆盖 Alias Token 实现,Component Token 自动跟随。落地时建议先建立颜色和间距的 Token 体系,再逐步扩展到排版、阴影和动效。命名规范和设计工具同步是长期运维的关键,建议在项目初期就建立自动化流程。

http://www.gsyq.cn/news/1532804.html

相关文章:

  • 梯度下降法数学理解
  • Novel-downloader:可扩展通用型小说下载解决方案的技术架构解析
  • MuleSoft AI编排:企业级LLM集成的七层可审计架构
  • Python闭包与装饰器的高级陷阱
  • Sqribble:面向知识工作者的文档操作系统与自动化交付方案
  • Python装饰器与描述符在ORM中的实现
  • 国内有哪些航空配餐类上市公司? - 品牌2026
  • 全球光模块龙头中际旭创300308:股价估值与基本面查询全攻略
  • Linux 调度器优化:从 CFS 到实时调度的性能调优实践
  • 【安徽大学主办,权威背书 | IEEE出版,EI 检索稳定 | 连续四届全部论文完成见刊检索,每届都在提交后2-3个月检索 | 设奖项评选】第五届半导体与电子技术国际研讨会(ISSET 2026)
  • D2R Pixel Bot:暗黑破坏神2重制版终极自动化解决方案
  • 2026年医院室内空气净化服务商推荐:病房与候诊区治理选型指南 - 观域传媒
  • 3步实现Windows电脑接收AirPlay投屏:完全免费开源方案指南
  • 如何轻松下载网页视频?这款免费Chrome插件3分钟帮你搞定
  • FoundationPose:零样本6D物体姿态估计基础模型实践指南
  • Windows Python 3.8下rasterio 1.3.10 wheel文件安装与GIS开发环境配置指南
  • 3个核心技术:解决STL到STEP格式转换的完整指南
  • 实战恶意软件分析:从动态行为监控到内存取证与自动化逆向
  • 2026年小草围挡与防腐彩涂板行业生态全景分析:从山东到西北的供应链与工程实践 - 优质品牌商家
  • codex添加第三方skills两种方法和使用方法
  • NSK直线导轨LH25BN升级NH25BN全指南
  • 2026年 广东LCD液晶显示屏厂家推荐榜单:车载屏/工控屏/医疗屏/数字标牌,专业显示技术实力派之选 - 品牌发掘
  • 杰理之Linein 采样延时优化【篇】
  • 靠谱软件外包公司到底好在哪
  • 逆变仿真全流程解析:从模型构建到实测验证的工程实践
  • 瑞芯微RK3576芯片开发全解析:从核心架构到AI模型部署实战
  • 小样本目标检测实战:100张标注+400张无标签数据构建可用模型
  • 抖音礼物图标PNG图片制作免抠图素材下载,2035个透明PNG素材打包分享(含等级图标、粉丝团图标、礼物图标)
  • Vulkan编程指南:高性能图形API的中文学习路径与技术决策分析
  • 如何快速修复损坏二维码:QRazyBox专业工具的完整解决方案