当 Token 有了三层骨骼:设计 Token 语义层级设计,从原始值到语义级再到组件级

当 Token 有了三层骨骼:设计 Token 语义层级设计,从原始值到语义级再到组件级

当 Token 有了三层骨骼:设计 Token 语义层级设计,从原始值到语义级再到组件级

一、深度引言与场景痛点

设计 Token 的层级设计像建筑的楼层——最底层是原始材料(砖块、水泥、钢筋),中间层是语义构件(承重墙、隔断墙、装饰面),顶层是成品组件(客厅、卧室、厨房)。如果组件直接引用原始材料(砖块),变更时需要逐一修改所有引用;如果组件引用语义构件(承重墙),变更时只需修改语义定义,原始材料的变化自动传递到所有使用语义构件的组件。

三层语义层级:原始层(primitive)定义视觉参数值,语义层(semantic)定义用途意图,组件层(component)定义具体组件的样式引用。三层之间的引用关系是单向链条——组件层引用语义层,语义层引用原始层,变更影响沿链条自下而上传递。

二、底层机制与原理深度剖析

flowchart TD subgraph Primitive["原始层(Primitive)"] P1["--blue-500: #3B82F6"] P2["--spacing-4: 16px"] P3["--font-size-lg: 20px"] end subgraph Semantic["语义层(Semantic)"] S1["--color-action-primary: var(--blue-500)"] S2["--spacing-card-gap: var(--spacing-4)"] S3["--text-size-heading: var(--font-size-lg)"] end subgraph Component["组件层(Component)"] C1["--button-bg: var(--color-action-primary)"] C2["--card-gap: var(--spacing-card-gap)"] C3["--card-title-size: var(--text-size-heading)"] end Primitive --> Semantic --> Component Note1["变更影响路径:<br/>修改 --blue-500 → 自动影响所有语义引用<br/>修改 --color-action-primary → 自动影响所有组件引用"] style P1 fill:#42a5f5 style S1 fill:#66bb6a style C1 fill:#ffa726

三层的核心价值:原始层修改不影响语义意图(把品牌色从蓝色改为红色,只需修改--color-action-primary的引用源,不需要逐个修改所有组件)。语义层修改不影响具体组件(把卡片间距从 16px 改为 24px,只需修改--spacing-card-gap的引用源)。变更影响被层级约束在最小范围内。

三、生产级代码实现与最佳实践

/* tokens.css — 三层语义层级完整定义 */ /* === 原始层:视觉参数值 === */ :root { /* 颜色原始值 */ --blue-500: #3B82F6; --blue-600: #2563EB; --gray-100: #F3F4F6; --gray-800: #1F2937; --green-500: #22C55E; --red-500: #EF4444; /* 间距原始值 */ --spacing-1: 4px; --spacing-2: 8px; --spacing-4: 16px; --spacing-6: 24px; --spacing-8: 32px; /* 字号原始值 */ --font-size-xs: 12px; --font-size-sm: 14px; --font-size-base: 16px; --font-size-lg: 20px; --font-size-xl: 24px; } /* === 语义层:用途意图 === */ :root { /* 颜色语义 */ --color-action-primary: var(--blue-500); --color-action-primary-hover: var(--blue-600); --color-text-primary: var(--gray-800); --color-text-secondary: var(--gray-100); --color-surface: #FFFFFF; --color-success: var(--green-500); --color-error: var(--red-500); /* 间距语义 */ --spacing-card-gap: var(--spacing-4); --spacing-button-padding: var(--spacing-2); --spacing-section-gap: var(--spacing-8); /* 字号语义 */ --text-size-heading: var(--font-size-lg); --text-size-body: var(--font-size-base); --text-size-caption: var(--font-size-sm); } /* === 组件层:具体组件样式 === */ :root { /* Button 组件 */ --button-background: var(--color-action-primary); --button-padding: var(--spacing-button-padding); --button-font-size: var(--text-size-body); /* Card 组件 */ --card-gap: var(--spacing-card-gap); --card-title-size: var(--text-size-heading); --card-description-color: var(--color-text-secondary); }

层级完整性检查脚本:

// scripts/token-hierarchy/check-hierarchy.ts import fs from 'fs'; function checkTokenHierarchy(tokensPath: string): void { const content = fs.readFileSync(tokensPath, 'utf-8'); const lines = content.split('\n'); const primitives: string[] = []; const semantics: string[] = []; const components: string[] = []; const violations: string[] = []; for (const line of lines) { const match = line.match(/^--([\w-]+):\s*(.+);$/); if (!match) continue; const [, name, value] = match; if (value.startsWith('var(--')) { // 引用其他 Token → 语义层或组件层 const referenced = value.match(/var\(--([\w-]+)\)/)?.[1]; // 检查组件层是否跳过语义层直接引用原始层 if (components.includes(`--${name}`) || name.includes('button') || name.includes('card')) { const refIsPrimitive = primitives.includes(`--${referenced}`); if (refIsPrimitive) { violations.push(`组件层 --${name} 直接引用原始层 --${referenced},应引用语义层 Token`); } } // 分类:引用原始值的归为语义层,引用语义值的归为组件层 if (primitives.includes(`--${referenced}`)) { semantics.push(`--${name}`); } else { components.push(`--${name}`); } } else { // 纯值 → 原始层 primitives.push(`--${name}`); } } if (violations.length > 0) { console.error('❌ Token 层级违规:'); violations.forEach(v => console.error(` ${v}`)); } else { console.log(`✅ Token 层级完整:原始 ${primitives.length} / 语义 ${semantics.length} / 组件 ${components.length}`); } }

四、边界分析与架构权衡

三层结构的管理成本。三层意味着同一份设计意图有三份声明——原始层声明色值、语义层声明引用、组件层声明引用。三份声明的维护成本比单层高,但变更时的修改范围比单层小。权衡:Token 数量少于 30 时三层结构过于冗余,可以用二层(原始 + 语义合并);Token 数量超过 50 时三层结构的变更隔离价值才显著。

语义层命名的精确度。--color-action-primary--color-primary更精确("主操作色"而非"主色"),但命名越长可读性越差。命名需要在精确度和可读性之间取平衡——核心语义用长命名(action-primary),常见语义用短命名(text-primary)。

组件层 Token 的覆盖范围。不是每个组件都需要组件层 Token——只有样式复杂且需要独立主题化的组件(Button、Card、Modal)才有组件层。简单的文本组件(Paragraph、Span)直接引用语义层即可,不需要中间加一层组件层。过度创建组件层 Token 会增加维护成本但不增加变更隔离价值。

五、总结

设计 Token 的三层语义层级是变更隔离的基础设施——原始层定义视觉参数值,语义层定义用途意图,组件层定义具体样式。三层之间的引用链条让变更影响沿层级自下而上传递,原始值的修改不需要逐个修改所有组件,只需修改语义层的引用源。

三层的核心价值是变更隔离——原始层修改不影响语义意图,语义层修改不影响组件样式,变更影响被约束在最小范围内。三层结构的管理成本比单层高,但变更时的修改成本比单层低。Token 数量超过 50 时三层结构的投资回报率最高——多维护两份声明,少改几十个组件。

层级完整性检查脚本确保三层引用链条不断裂——组件层跳过语义层直接引用原始层是违规行为,检查脚本自动发现并报警。层级不是建议而是约束,每个组件必须通过语义层间接引用原始值,这是设计系统变更可控性的底线规则。