原子化设计实践从设计 Token 到可组合组件的工程化体系一、组件碎片化的困境为什么传统设计系统难以规模化设计系统的核心承诺是一次设计到处复用。但在实际规模化过程中传统的设计系统架构——将组件按功能层级划分为 Atoms、Molecules、Organisms——往往面临一个悖论组件层级越高复用性越低层级越低组合成本越高。具体表现为三个典型问题第一组件膨胀——一个卡片组件为了适配所有业务场景不断累积变体逻辑最终演变为一个包含 20 props 的上帝组件维护成本远超收益第二样式冲突——不同业务团队在使用同一组件时通过!important或 CSS 覆盖来定制样式导致设计系统的一致性承诺形同虚设第三Token 与组件的割裂——设计 Token 定义了颜色、间距、字体等基础变量但组件内部仍然存在大量硬编码的魔法数值Token 体系未能真正约束组件的视觉输出。原子化设计的工程化实践核心目标是将设计系统从预置组件库转变为可组合的原子集合——通过 Design Token 严格约束视觉一致性通过原子组件提供最小粒度的可组合单元通过组合模式替代继承模式来构建业务组件。二、原子化设计的分层架构Token → 原子 → 组合flowchart TB A[Design Token 层] -- B[原子组件层] B -- C[组合模式层] C -- D[业务组件层] A -- A1[颜色 Token] A -- A2[间距 Token] A -- A3[字体 Token] A -- A4[圆角/阴影 Token] A -- A5[动效曲线 Token] B -- B1[Text 原子] B -- B2[Box 原子] B -- B3[Icon 原子] B -- B4[Stack 原子] C -- C1[Slot 模式] C -- C2[Recipe 模式] C -- C3[Variant 模式] D -- D1[Card 组件] D -- D2[ListItem 组件] D -- D3[FormField 组件] style A fill:#e8f4f8,stroke:#2196F3 style B fill:#e8f5e9,stroke:#4CAF50 style C fill:#fff3e0,stroke:#FF9800 style D fill:#f3e5f5,stroke:#9C27B0原子化设计的分层架构遵循三个核心原则Token 先行。所有视觉属性必须通过 Token 引用组件中不允许出现任何硬编码的颜色值、间距值或字体大小。Token 是设计系统的宪法组件是法律业务代码是判例——宪法不可违反法律可以扩展判例可以灵活。原子最小化。原子组件只负责一个视觉职责Text只管文字渲染Box只管容器样式Icon只管图标展示。原子组件不包含业务逻辑不预设布局结构通过 props 暴露所有可定制点。组合优于继承。业务组件通过组合原子组件构建而非继承基础组件扩展。组合模式允许业务组件自由选择需要的原子避免继承链中不需要的功能被强制带入。三、生产级实现基于 CSS 变量的原子化设计系统以下是一个完整的原子化设计系统实现涵盖 Token 定义、原子组件和组合模式/* * 第一层Design Token 定义 * 所有视觉属性的唯一真相来源 * */ :root { /* --- 颜色系统 --- */ --color-primary-50: #eef2ff; --color-primary-100: #e0e7ff; --color-primary-200: #c7d2fe; --color-primary-300: #a5b4fc; --color-primary-400: #818cf8; --color-primary-500: #6366f1; --color-primary-600: #4f46e5; --color-primary-700: #4338ca; --color-primary-800: #3730a3; --color-primary-900: #312e81; --color-primary-950: #1e1b4b; --color-neutral-0: #ffffff; --color-neutral-50: #f8fafc; --color-neutral-100: #f1f5f9; --color-neutral-200: #e2e8f0; --color-neutral-300: #cbd5e1; --color-neutral-400: #94a3b8; --color-neutral-500: #64748b; --color-neutral-600: #475569; --color-neutral-700: #334155; --color-neutral-800: #1e293b; --color-neutral-900: #0f172a; --color-neutral-950: #020617; /* 语义色引用基础色板而非硬编码 */ --color-surface: var(--color-neutral-0); --color-surface-secondary: var(--color-neutral-50); --color-on-surface: var(--color-neutral-900); --color-on-surface-secondary: var(--color-neutral-500); --color-border: var(--color-neutral-200); --color-accent: var(--color-primary-500); /* --- 间距系统4px 基础单位 --- */ --space-0: 0; --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px; --space-12: 48px; --space-16: 64px; /* --- 字体系统 --- */ --font-sans: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; --font-mono: JetBrains Mono, Fira Code, monospace; --text-xs: 0.75rem; /* 12px */ --text-sm: 0.875rem; /* 14px */ --text-base: 1rem; /* 16px */ --text-lg: 1.125rem; /* 18px */ --text-xl: 1.25rem; /* 20px */ --text-2xl: 1.5rem; /* 24px */ --text-3xl: 1.875rem; /* 30px */ --leading-none: 1; --leading-tight: 1.25; --leading-normal: 1.5; --leading-relaxed: 1.75; --tracking-tight: -0.025em; --tracking-normal: 0; --tracking-wide: 0.025em; /* --- 圆角系统 --- */ --radius-none: 0; --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* --- 阴影系统 --- */ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.04); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.08), 0 4px 6px rgba(0, 0, 0, 0.04); /* --- 动效系统 --- */ --ease-default: cubic-bezier(0.4, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); --duration-fast: 150ms; --duration-normal: 250ms; --duration-slow: 400ms; }/* * 第二层原子组件 * 每个原子只负责一个视觉职责 * */ /* Text 原子文字渲染的唯一出口 */ .text { font-family: var(--font-sans); color: var(--color-on-surface); line-height: var(--leading-normal); margin: 0; } .text--xs { font-size: var(--text-xs); } .text--sm { font-size: var(--text-sm); } .text--base { font-size: var(--text-base); } .text--lg { font-size: var(--text-lg); } .text--xl { font-size: var(--text-xl); } .text--2xl { font-size: var(--text-2xl); } .text--3xl { font-size: var(--text-3xl); } .text--secondary { color: var(--color-on-surface-secondary); } .text--accent { color: var(--color-accent); } .text--tight { line-height: var(--leading-tight); } .text--relaxed { line-height: var(--leading-relaxed); } .text--medium { font-weight: 500; } .text--semibold { font-weight: 600; } .text--bold { font-weight: 700; } /* Box 原子容器的通用样式基类 */ .box { background: var(--color-surface); border-radius: var(--radius-md); border: 1px solid var(--color-border); } .box--elevated { box-shadow: var(--shadow-md); border-color: transparent; } .box--flat { background: var(--color-surface-secondary); border-color: transparent; } .box--rounded { border-radius: var(--radius-lg); } .box--pill { border-radius: var(--radius-full); } /* 间距原子使用 Token 而非硬编码 */ .p-2 { padding: var(--space-2); } .p-4 { padding: var(--space-4); } .p-6 { padding: var(--space-6); } .p-8 { padding: var(--space-8); } .px-4 { padding-left: var(--space-4); padding-right: var(--space-4); } .py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); } .py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); } .gap-2 { gap: var(--space-2); } .gap-4 { gap: var(--space-4); } .gap-6 { gap: var(--space-6); } /* Stack 原子布局容器 */ .stack { display: flex; } .stack--horizontal { flex-direction: row; align-items: center; } .stack--vertical { flex-direction: column; } .stack--center { align-items: center; justify-content: center; } .stack--between { justify-content: space-between; } .stack--wrap { flex-wrap: wrap; }!-- 第三层组合模式——用原子组装业务组件 -- !-- 模式一Slot 组合——通过插槽注入自定义内容 -- div classbox box--elevated p-6 div classstack stack--vertical gap-4 !-- 标题区域 Slot -- div classstack stack--horizontal stack--between h3 classtext text--lg text--semibold text--tight 项目概览 /h3 span classtext text--sm text--secondary 最近更新 /span /div !-- 内容区域 Slot -- div classstack stack--vertical gap-2 p classtext text--base text--secondary 原子化设计通过 Token 约束视觉一致性 通过原子组件提供最小粒度的可组合单元。 /p /div !-- 操作区域 Slot -- div classstack stack--horizontal gap-4 stylemargin-top: auto; button classbox box--pill px-4 py-2 text text--sm text--medium stylebackground: var(--color-accent); color: white; border: none; cursor: pointer; 查看详情 /button button classbox box--pill px-4 py-2 text text--sm text--secondary stylebackground: transparent; cursor: pointer; 稍后再看 /button /div /div /div !-- 模式二列表项组合——原子堆叠构建复杂结构 -- div classstack stack--vertical gap-2 div classbox box--flat p-4 div classstack stack--horizontal gap-4 stack--center div classbox box--rounded stylewidth: 40px; height: 40px; background: var(--color-primary-100); display: flex; align-items: center; justify-content: center; span classtext text--sm text--accent text--semibold01/span /div div classstack stack--vertical gap-1 styleflex: 1; span classtext text--base text--semibold设计 Token 体系搭建/span span classtext text--sm text--secondary定义颜色、间距、字体等基础变量/span /div span classtext text--xs text--secondary已完成/span /div /div /div/** * 第四层Token 校验工具 * 确保组件中不存在硬编码的视觉值 */ // 定义允许的 Token 前缀 const TOKEN_PREFIXES [ --color-, --space-, --text-, --radius-, --shadow-, --ease-, --duration-, ]; // 禁止在组件样式中出现的硬编码模式 const FORBIDDEN_PATTERNS [ /#[0-9a-fA-F]{3,8}/, // 硬编码颜色值 /\dpx(?!\s*[;}\n])/, // 硬编码像素值排除 CSS 变量引用中的 /\drem(?!\s*[;}\n])/, // 硬编码 rem 值 /rgba?\([^)]\)/, // 硬编码 rgba 值 ]; /** * 校验 CSS 文件中是否存在硬编码的视觉值 * param {string} cssContent - CSS 文件内容 * returns {Array{line: number, value: string, suggestion: string}} 违规列表 */ function validateTokenUsage(cssContent) { const violations []; const lines cssContent.split(\n); lines.forEach((line, index) { // 跳过注释行和 Token 定义行 if (line.trim().startsWith(/*) || line.includes(:root)) return; // 跳过 CSS 变量引用行var(--xxx) if (line.includes(var()) return; FORBIDDEN_PATTERNS.forEach((pattern) { const match line.match(pattern); if (match) { violations.push({ line: index 1, value: match[0], context: line.trim(), suggestion: 请使用 Design Token 替代硬编码值 ${match[0]}, }); } }); }); return violations; } /** * 生成 Token 使用报告 * 统计组件中 Token 引用的覆盖率 */ function generateTokenReport(cssContent) { const tokenRefs cssContent.match(/var\([^)]\)/g) || []; const uniqueTokens [...new Set(tokenRefs)]; // 按类别分组 const grouped {}; uniqueTokens.forEach((token) { const category TOKEN_PREFIXES.find((prefix) token.includes(prefix) ) || other; if (!grouped[category]) grouped[category] []; grouped[category].push(token); }); return { totalTokenRefs: tokenRefs.length, uniqueTokens: uniqueTokens.length, categories: grouped, coverage: (uniqueTokens.length / 50 * 100).toFixed(1) %, // 假设设计系统定义了 50 个核心 Token }; }上述实现的关键设计决策Token 作为唯一真相来源。所有视觉属性通过 CSS 变量定义组件中不出现任何硬编码的颜色值或间距值。语义色如--color-accent引用基础色板如--color-primary-500而非直接定义色值。这种间接引用使得全局换肤只需修改基础色板语义色自动跟随变化。原子组件的职责单一性。Text原子只管文字渲染Box原子只管容器样式Stack原子只管布局排列。通过修饰符类如text--lg、box--elevated组合变体而非在原子内部通过条件逻辑切换。这种模式确保了原子的可预测性——组合结果总是可预期的。Slot 组合模式。业务组件通过 Slot 模式组装原子而非通过继承扩展。卡片组件不预设标题区 内容区 操作区的固定结构而是由使用方通过stack原子自由组合。这消除了上帝组件的问题——卡片组件不需要 20 props 来适配所有场景。四、原子化设计的工程权衡组合复杂度与开发效率的矛盾。原子化设计将组合权交给使用方这意味着开发者需要理解原子的组合规则才能构建业务组件。在团队初期这可能导致开发效率下降——原本一个Card组件就能解决的问题现在需要手动组合boxstacktext等多个原子。建议通过 Recipe 模式预定义的原子组合配方来缓解这个问题将常用的组合模式封装为可复用的模板。CSS 类名膨胀。原子化设计会产生大量的工具类如p-4、text--lgHTML 中类名列表可能变得冗长。这在可读性上是一种退步但换来了组合灵活性和样式一致性。可以通过 CSS Modules 或构建时工具如 Tailwind CSS 的 JIT 模式来控制最终产物体积——未使用的原子类不会出现在生产构建中。Token 粒度的平衡。Token 定义过细如为每个组件定义专属间距 Token会导致维护成本激增定义过粗如只定义--space-sm和--space-lg则无法约束组件的视觉精度。建议采用基础 Token 语义 Token的两层结构基础 Token 定义所有可用值语义 Token 为特定场景引用基础值。组件只引用语义 Token不直接引用基础 Token。设计工具的同步挑战。Figma 中的设计变量需要与代码中的 Token 保持同步。目前主流方案是通过 Figma Variables API 导出 Token再通过 Style Dictionary 转换为 CSS 变量。但这个链路中的格式转换和命名映射仍需人工维护完全自动化的端到端同步尚不成熟。五、总结原子化设计的工程化实践核心是将设计系统从预置组件库重构为Token 原子 组合的三层架构。Design Token 确保视觉一致性原子组件提供最小粒度的可组合单元组合模式替代继承模式来构建业务组件。落地路线上建议分三步推进第一步建立完整的 Token 体系并集成到构建流程中确保所有视觉值都有唯一的 Token 引用第二步封装核心原子组件Text、Box、Stack、Icon并在 2-3 个业务页面中验证组合模式的可行性第三步建立 Token 校验工具和 Recipe 库将常用的组合模式固化为可复用的配方降低团队的使用门槛。关键原则是 Token 不可绕过原子不可拆分组合优于继承。
原子化设计实践:从设计 Token 到可组合组件的工程化体系
发布时间:2026/7/1 4:46:17
原子化设计实践从设计 Token 到可组合组件的工程化体系一、组件碎片化的困境为什么传统设计系统难以规模化设计系统的核心承诺是一次设计到处复用。但在实际规模化过程中传统的设计系统架构——将组件按功能层级划分为 Atoms、Molecules、Organisms——往往面临一个悖论组件层级越高复用性越低层级越低组合成本越高。具体表现为三个典型问题第一组件膨胀——一个卡片组件为了适配所有业务场景不断累积变体逻辑最终演变为一个包含 20 props 的上帝组件维护成本远超收益第二样式冲突——不同业务团队在使用同一组件时通过!important或 CSS 覆盖来定制样式导致设计系统的一致性承诺形同虚设第三Token 与组件的割裂——设计 Token 定义了颜色、间距、字体等基础变量但组件内部仍然存在大量硬编码的魔法数值Token 体系未能真正约束组件的视觉输出。原子化设计的工程化实践核心目标是将设计系统从预置组件库转变为可组合的原子集合——通过 Design Token 严格约束视觉一致性通过原子组件提供最小粒度的可组合单元通过组合模式替代继承模式来构建业务组件。二、原子化设计的分层架构Token → 原子 → 组合flowchart TB A[Design Token 层] -- B[原子组件层] B -- C[组合模式层] C -- D[业务组件层] A -- A1[颜色 Token] A -- A2[间距 Token] A -- A3[字体 Token] A -- A4[圆角/阴影 Token] A -- A5[动效曲线 Token] B -- B1[Text 原子] B -- B2[Box 原子] B -- B3[Icon 原子] B -- B4[Stack 原子] C -- C1[Slot 模式] C -- C2[Recipe 模式] C -- C3[Variant 模式] D -- D1[Card 组件] D -- D2[ListItem 组件] D -- D3[FormField 组件] style A fill:#e8f4f8,stroke:#2196F3 style B fill:#e8f5e9,stroke:#4CAF50 style C fill:#fff3e0,stroke:#FF9800 style D fill:#f3e5f5,stroke:#9C27B0原子化设计的分层架构遵循三个核心原则Token 先行。所有视觉属性必须通过 Token 引用组件中不允许出现任何硬编码的颜色值、间距值或字体大小。Token 是设计系统的宪法组件是法律业务代码是判例——宪法不可违反法律可以扩展判例可以灵活。原子最小化。原子组件只负责一个视觉职责Text只管文字渲染Box只管容器样式Icon只管图标展示。原子组件不包含业务逻辑不预设布局结构通过 props 暴露所有可定制点。组合优于继承。业务组件通过组合原子组件构建而非继承基础组件扩展。组合模式允许业务组件自由选择需要的原子避免继承链中不需要的功能被强制带入。三、生产级实现基于 CSS 变量的原子化设计系统以下是一个完整的原子化设计系统实现涵盖 Token 定义、原子组件和组合模式/* * 第一层Design Token 定义 * 所有视觉属性的唯一真相来源 * */ :root { /* --- 颜色系统 --- */ --color-primary-50: #eef2ff; --color-primary-100: #e0e7ff; --color-primary-200: #c7d2fe; --color-primary-300: #a5b4fc; --color-primary-400: #818cf8; --color-primary-500: #6366f1; --color-primary-600: #4f46e5; --color-primary-700: #4338ca; --color-primary-800: #3730a3; --color-primary-900: #312e81; --color-primary-950: #1e1b4b; --color-neutral-0: #ffffff; --color-neutral-50: #f8fafc; --color-neutral-100: #f1f5f9; --color-neutral-200: #e2e8f0; --color-neutral-300: #cbd5e1; --color-neutral-400: #94a3b8; --color-neutral-500: #64748b; --color-neutral-600: #475569; --color-neutral-700: #334155; --color-neutral-800: #1e293b; --color-neutral-900: #0f172a; --color-neutral-950: #020617; /* 语义色引用基础色板而非硬编码 */ --color-surface: var(--color-neutral-0); --color-surface-secondary: var(--color-neutral-50); --color-on-surface: var(--color-neutral-900); --color-on-surface-secondary: var(--color-neutral-500); --color-border: var(--color-neutral-200); --color-accent: var(--color-primary-500); /* --- 间距系统4px 基础单位 --- */ --space-0: 0; --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px; --space-12: 48px; --space-16: 64px; /* --- 字体系统 --- */ --font-sans: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; --font-mono: JetBrains Mono, Fira Code, monospace; --text-xs: 0.75rem; /* 12px */ --text-sm: 0.875rem; /* 14px */ --text-base: 1rem; /* 16px */ --text-lg: 1.125rem; /* 18px */ --text-xl: 1.25rem; /* 20px */ --text-2xl: 1.5rem; /* 24px */ --text-3xl: 1.875rem; /* 30px */ --leading-none: 1; --leading-tight: 1.25; --leading-normal: 1.5; --leading-relaxed: 1.75; --tracking-tight: -0.025em; --tracking-normal: 0; --tracking-wide: 0.025em; /* --- 圆角系统 --- */ --radius-none: 0; --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* --- 阴影系统 --- */ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.04); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.08), 0 4px 6px rgba(0, 0, 0, 0.04); /* --- 动效系统 --- */ --ease-default: cubic-bezier(0.4, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); --duration-fast: 150ms; --duration-normal: 250ms; --duration-slow: 400ms; }/* * 第二层原子组件 * 每个原子只负责一个视觉职责 * */ /* Text 原子文字渲染的唯一出口 */ .text { font-family: var(--font-sans); color: var(--color-on-surface); line-height: var(--leading-normal); margin: 0; } .text--xs { font-size: var(--text-xs); } .text--sm { font-size: var(--text-sm); } .text--base { font-size: var(--text-base); } .text--lg { font-size: var(--text-lg); } .text--xl { font-size: var(--text-xl); } .text--2xl { font-size: var(--text-2xl); } .text--3xl { font-size: var(--text-3xl); } .text--secondary { color: var(--color-on-surface-secondary); } .text--accent { color: var(--color-accent); } .text--tight { line-height: var(--leading-tight); } .text--relaxed { line-height: var(--leading-relaxed); } .text--medium { font-weight: 500; } .text--semibold { font-weight: 600; } .text--bold { font-weight: 700; } /* Box 原子容器的通用样式基类 */ .box { background: var(--color-surface); border-radius: var(--radius-md); border: 1px solid var(--color-border); } .box--elevated { box-shadow: var(--shadow-md); border-color: transparent; } .box--flat { background: var(--color-surface-secondary); border-color: transparent; } .box--rounded { border-radius: var(--radius-lg); } .box--pill { border-radius: var(--radius-full); } /* 间距原子使用 Token 而非硬编码 */ .p-2 { padding: var(--space-2); } .p-4 { padding: var(--space-4); } .p-6 { padding: var(--space-6); } .p-8 { padding: var(--space-8); } .px-4 { padding-left: var(--space-4); padding-right: var(--space-4); } .py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); } .py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); } .gap-2 { gap: var(--space-2); } .gap-4 { gap: var(--space-4); } .gap-6 { gap: var(--space-6); } /* Stack 原子布局容器 */ .stack { display: flex; } .stack--horizontal { flex-direction: row; align-items: center; } .stack--vertical { flex-direction: column; } .stack--center { align-items: center; justify-content: center; } .stack--between { justify-content: space-between; } .stack--wrap { flex-wrap: wrap; }!-- 第三层组合模式——用原子组装业务组件 -- !-- 模式一Slot 组合——通过插槽注入自定义内容 -- div classbox box--elevated p-6 div classstack stack--vertical gap-4 !-- 标题区域 Slot -- div classstack stack--horizontal stack--between h3 classtext text--lg text--semibold text--tight 项目概览 /h3 span classtext text--sm text--secondary 最近更新 /span /div !-- 内容区域 Slot -- div classstack stack--vertical gap-2 p classtext text--base text--secondary 原子化设计通过 Token 约束视觉一致性 通过原子组件提供最小粒度的可组合单元。 /p /div !-- 操作区域 Slot -- div classstack stack--horizontal gap-4 stylemargin-top: auto; button classbox box--pill px-4 py-2 text text--sm text--medium stylebackground: var(--color-accent); color: white; border: none; cursor: pointer; 查看详情 /button button classbox box--pill px-4 py-2 text text--sm text--secondary stylebackground: transparent; cursor: pointer; 稍后再看 /button /div /div /div !-- 模式二列表项组合——原子堆叠构建复杂结构 -- div classstack stack--vertical gap-2 div classbox box--flat p-4 div classstack stack--horizontal gap-4 stack--center div classbox box--rounded stylewidth: 40px; height: 40px; background: var(--color-primary-100); display: flex; align-items: center; justify-content: center; span classtext text--sm text--accent text--semibold01/span /div div classstack stack--vertical gap-1 styleflex: 1; span classtext text--base text--semibold设计 Token 体系搭建/span span classtext text--sm text--secondary定义颜色、间距、字体等基础变量/span /div span classtext text--xs text--secondary已完成/span /div /div /div/** * 第四层Token 校验工具 * 确保组件中不存在硬编码的视觉值 */ // 定义允许的 Token 前缀 const TOKEN_PREFIXES [ --color-, --space-, --text-, --radius-, --shadow-, --ease-, --duration-, ]; // 禁止在组件样式中出现的硬编码模式 const FORBIDDEN_PATTERNS [ /#[0-9a-fA-F]{3,8}/, // 硬编码颜色值 /\dpx(?!\s*[;}\n])/, // 硬编码像素值排除 CSS 变量引用中的 /\drem(?!\s*[;}\n])/, // 硬编码 rem 值 /rgba?\([^)]\)/, // 硬编码 rgba 值 ]; /** * 校验 CSS 文件中是否存在硬编码的视觉值 * param {string} cssContent - CSS 文件内容 * returns {Array{line: number, value: string, suggestion: string}} 违规列表 */ function validateTokenUsage(cssContent) { const violations []; const lines cssContent.split(\n); lines.forEach((line, index) { // 跳过注释行和 Token 定义行 if (line.trim().startsWith(/*) || line.includes(:root)) return; // 跳过 CSS 变量引用行var(--xxx) if (line.includes(var()) return; FORBIDDEN_PATTERNS.forEach((pattern) { const match line.match(pattern); if (match) { violations.push({ line: index 1, value: match[0], context: line.trim(), suggestion: 请使用 Design Token 替代硬编码值 ${match[0]}, }); } }); }); return violations; } /** * 生成 Token 使用报告 * 统计组件中 Token 引用的覆盖率 */ function generateTokenReport(cssContent) { const tokenRefs cssContent.match(/var\([^)]\)/g) || []; const uniqueTokens [...new Set(tokenRefs)]; // 按类别分组 const grouped {}; uniqueTokens.forEach((token) { const category TOKEN_PREFIXES.find((prefix) token.includes(prefix) ) || other; if (!grouped[category]) grouped[category] []; grouped[category].push(token); }); return { totalTokenRefs: tokenRefs.length, uniqueTokens: uniqueTokens.length, categories: grouped, coverage: (uniqueTokens.length / 50 * 100).toFixed(1) %, // 假设设计系统定义了 50 个核心 Token }; }上述实现的关键设计决策Token 作为唯一真相来源。所有视觉属性通过 CSS 变量定义组件中不出现任何硬编码的颜色值或间距值。语义色如--color-accent引用基础色板如--color-primary-500而非直接定义色值。这种间接引用使得全局换肤只需修改基础色板语义色自动跟随变化。原子组件的职责单一性。Text原子只管文字渲染Box原子只管容器样式Stack原子只管布局排列。通过修饰符类如text--lg、box--elevated组合变体而非在原子内部通过条件逻辑切换。这种模式确保了原子的可预测性——组合结果总是可预期的。Slot 组合模式。业务组件通过 Slot 模式组装原子而非通过继承扩展。卡片组件不预设标题区 内容区 操作区的固定结构而是由使用方通过stack原子自由组合。这消除了上帝组件的问题——卡片组件不需要 20 props 来适配所有场景。四、原子化设计的工程权衡组合复杂度与开发效率的矛盾。原子化设计将组合权交给使用方这意味着开发者需要理解原子的组合规则才能构建业务组件。在团队初期这可能导致开发效率下降——原本一个Card组件就能解决的问题现在需要手动组合boxstacktext等多个原子。建议通过 Recipe 模式预定义的原子组合配方来缓解这个问题将常用的组合模式封装为可复用的模板。CSS 类名膨胀。原子化设计会产生大量的工具类如p-4、text--lgHTML 中类名列表可能变得冗长。这在可读性上是一种退步但换来了组合灵活性和样式一致性。可以通过 CSS Modules 或构建时工具如 Tailwind CSS 的 JIT 模式来控制最终产物体积——未使用的原子类不会出现在生产构建中。Token 粒度的平衡。Token 定义过细如为每个组件定义专属间距 Token会导致维护成本激增定义过粗如只定义--space-sm和--space-lg则无法约束组件的视觉精度。建议采用基础 Token 语义 Token的两层结构基础 Token 定义所有可用值语义 Token 为特定场景引用基础值。组件只引用语义 Token不直接引用基础 Token。设计工具的同步挑战。Figma 中的设计变量需要与代码中的 Token 保持同步。目前主流方案是通过 Figma Variables API 导出 Token再通过 Style Dictionary 转换为 CSS 变量。但这个链路中的格式转换和命名映射仍需人工维护完全自动化的端到端同步尚不成熟。五、总结原子化设计的工程化实践核心是将设计系统从预置组件库重构为Token 原子 组合的三层架构。Design Token 确保视觉一致性原子组件提供最小粒度的可组合单元组合模式替代继承模式来构建业务组件。落地路线上建议分三步推进第一步建立完整的 Token 体系并集成到构建流程中确保所有视觉值都有唯一的 Token 引用第二步封装核心原子组件Text、Box、Stack、Icon并在 2-3 个业务页面中验证组合模式的可行性第三步建立 Token 校验工具和 Recipe 库将常用的组合模式固化为可复用的配方降低团队的使用门槛。关键原则是 Token 不可绕过原子不可拆分组合优于继承。