面向设计系统的 AI 迭代:组件一致性审查与设计 Token 自动化管理
设计系统在规模化团队中的最大挑战不是"如何创建",而是"如何保持"。随着产品迭代,组件变体不断膨胀,设计 Token 的定义与使用逐渐偏离规范——设计师看到的 Figma 文件与开发者实现的组件之间存在越来越大的鸿沟。AI 的能力恰好可以填补这道鸿沟:通过自动化的一致性和 Token 审查,将"设计规范"从文档变成可执行约束。
一、设计系统迭代的典型痛点
在一个 20+ 人、3 条产品线的团队中,设计系统的维护面临以下核心问题:
二、设计 Token 的自动化管理
2.1 Token 的数据模型
将设计 Token 作为单一数据源,从 Figma 导出并自动同步到代码:
/**
* 设计 Token 的标准数据结构
* 遵循 W3C Design Tokens Community Group 规范
*/
interface DesignToken {
/** Token 名称,遵循 kebab-case 命名 */
name: string;
/** Token 值 */
value: string | number;
/** Token 类型 */
type: 'color' | 'spacing' | 'typography' | 'borderRadius' | 'shadow' | 'opacity';
/** 语义分组 */
group: string;
/** 描述 */
description?: string;
/** Figma 中的引用路径(可追溯来源) */
figmaRef?: {
fileId: string;
nodeId: string;
updatedAt: string;
};
/** 是否已废弃 */
deprecated?: boolean;
}
/**
* Token 集合的完整定义
*/
interface TokenCollection {
version: string;
updatedAt: string;
tokens: DesignToken[];
}
2.2 Token 同步管线
2.3 Token 同步引擎实现
/**
* Token 同步引擎
* 从 Figma 提取 Token 并与代码仓库中的 Token 对比
*/
interface SyncReport {
added: DesignToken[];
modified: DesignToken[];
deprecated: DesignToken[];
unchanged: number;
conflicts: Array<{ token: DesignToken; reason: string }>;
}
class TokenSyncEngine {
private figmaToken: string;
private repoTokenPath: string;
constructor(figmaToken: string, repoTokenPath: string) {
this.figmaToken = figmaToken;
this.repoTokenPath = repoTokenPath;
}
/**
* 执行完整的同步流程
*/
async sync(figmaFileId: string): Promise<SyncReport> {
// 1. 从 Figma 提取 Token
const figmaTokens = await this.extractFromFigma(figmaFileId);
// 2. 读取代码仓库中的当前 Token
const repoTokens = await this.readRepoTokens();
// 3. 执行对比
const report = this.compareTokens(figmaTokens, repoTokens);
// 4. 验证 Token 命名规范
const namingViolations = this.validateNamingConvention(figmaTokens);
if (namingViolations.length > 0) {
report.conflicts.push(
…namingViolations.map(v => ({
token: v,
reason: `命名不符合规范: ${v.name}`,
}))
);
}
return report;
}
/**
* 从 Figma API 提取 Token
*/
private async extractFromFigma(fileId: string): Promise<DesignToken[]> {
try {
const response = await fetch(
`https://api.figma.com/v1/files/${fileId}`,
{ headers: { 'X-Figma-Token': this.figmaToken } }
);
if (!response.ok) {
throw new Error(`Figma API 请求失败: ${response.status}`);
}
const data = await response.json();
return this.parseFigmaTokens(data);
} catch (error) {
console.error('[TokenSync] Figma Token 提取失败', error);
throw error;
}
}
/**
* 解析 Figma 响应中的 Token
*/
private parseFigmaTokens(figmaData: any): DesignToken[] {
const tokens: DesignToken[] = [];
// 实际实现需要递归遍历 Figma 的节点树
// 查找带有 "token/" 前缀的样式和变量
const styles = figmaData.styles || {};
for (const [id, style] of Object.entries(styles) as [string, any][]) {
// 只提取命名符合 Token 规范的样式
if (style.name.startsWith('token/')) {
tokens.push({
name: style.name.replace('token/', ''),
value: style.description || '', // Figma 样式的 value 需要从 fill/stroke 中解析
type: this.inferTokenType(style),
group: this.extractGroup(style.name),
description: style.description,
figmaRef: {
fileId: figmaData.documentId,
nodeId: id,
updatedAt: style.updatedAt || new Date().toISOString(),
},
});
}
}
return tokens;
}
/**
* 对比 Figma Token 与仓库 Token
*/
private compareTokens(figmaTokens: DesignToken[], repoTokens: DesignToken[]): SyncReport {
const report: SyncReport = {
added: [],
modified: [],
deprecated: [],
unchanged: 0,
conflicts: [],
};
const repoMap = new Map(repoTokens.map(t => [t.name, t]));
const figmaMap = new Map(figmaTokens.map(t => [t.name, t]));
// 检测新增和修改
for (const figmaToken of figmaTokens) {
const repoToken = repoMap.get(figmaToken.name);
if (!repoToken) {
report.added.push(figmaToken);
} else if (repoToken.value !== figmaToken.value) {
report.modified.push(figmaToken);
} else {
report.unchanged++;
}
}
// 检测废弃:在仓库中存在但 Figma 中已删除的 Token
for (const repoToken of repoTokens) {
if (!figmaMap.has(repoToken.name) && !repoToken.deprecated) {
report.deprecated.push({ …repoToken, deprecated: true });
}
}
return report;
}
/**
* 验证 Token 命名是否遵循规范
* 规范:{group}-{property}-{variant}-{state}
*/
private validateNamingConvention(tokens: DesignToken[]): DesignToken[] {
const pattern = /^[a-z]+(-[a-z0-9]+){2,4}$/;
return tokens.filter(t => !pattern.test(t.name));
}
private inferTokenType(style: any): DesignToken['type'] {
if (style.styleType === 'FILL') return 'color';
if (style.styleType === 'TEXT') return 'typography';
return 'color';
}
private extractGroup(name: string): string {
return name.split('/')[0] || 'global';
}
private async readRepoTokens(): Promise<DesignToken[]> {
// 实际实现中从文件系统读取 JSON/YAML 格式的 Token 文件
return [];
}
}
三、AI 驱动的组件一致性审查
3.1 审查维度
组件一致性审查覆盖三个维度:视觉一致性(颜色、间距、圆角是否匹配 Token)、行为一致性(交互状态是否完整)、语义一致性(组件含义是否与设计定义一致)。
/**
* 组件一致性的审查规则
*/
interface ConsistencyRule {
id: string;
name: string;
description: string;
/** 检查逻辑 */
check: (component: ComponentInfo, tokens: DesignToken[]) => ConsistencyViolation[];
/** 严重级别 */
severity: 'error' | 'warning' | 'info';
}
interface ComponentInfo {
name: string;
filePath: string;
/** 使用的 CSS 属性 */
styles: Record<string, string>;
/** 组件的交互状态列表 */
states: string[];
/** 相关联的 Token 名称 */
usedTokens: string[];
}
interface ConsistencyViolation {
ruleId: string;
component: string;
filePath: string;
message: string;
severity: 'error' | 'warning' | 'info';
/** 修复建议 */
suggestion: string;
/** 涉及的 Token */
affectedToken?: string;
/** 当前值 */
currentValue?: string;
/** 期望值 */
expectedValue?: string;
}
3.2 审查引擎
/**
* AI 驱动的组件一致性审查引擎
*/
class ConsistencyAuditor {
private rules: ConsistencyRule[] = [];
private tokens: DesignToken[] = [];
private llmEndpoint: string;
constructor(llmEndpoint: string = '/api/ai/consistency-audit') {
this.llmEndpoint = llmEndpoint;
this.registerDefaultRules();
}
/**
* 对组件列表执行全面审查
*/
async audit(components: ComponentInfo[]): Promise<ConsistencyViolation[]> {
const violations: ConsistencyViolation[] = [];
// 1. 基于规则的静态检查
for (const component of components) {
for (const rule of this.rules) {
try {
const result = rule.check(component, this.tokens);
violations.push(…result);
} catch (error) {
console.error(`[Auditor] 规则 ${rule.id} 执行异常`, error);
}
}
}
// 2. AI 辅助的语义一致性检查
try {
const aiViolations = await this.aiConsistencyCheck(components);
violations.push(…aiViolations);
} catch (error) {
console.error('[Auditor] AI 审查失败,仅返回规则检查结果', error);
}
return violations;
}
/**
* 注册默认审查规则
*/
private registerDefaultRules(): void {
// 规则:检查颜色值是否来自 Token 而非硬编码
this.rules.push({
id: 'color-hardcoded',
name: '颜色硬编码检测',
description: '检测组件中是否使用了未定义 Token 的颜色值',
severity: 'error',
check: (component, tokens) => {
const violations: ConsistencyViolation[] = [];
const tokenColors = new Set(tokens.filter(t => t.type === 'color').map(t => t.value));
// 检查 CSS 中的颜色值
const colorProps = ['color', 'backgroundColor', 'borderColor', 'boxShadow'];
for (const prop of colorProps) {
const value = component.styles[prop];
if (value && !value.startsWith('var(–') && !tokenColors.has(value)) {
violations.push({
ruleId: 'color-hardcoded',
component: component.name,
filePath: component.filePath,
message: `${component.name} 的 ${prop} 使用了硬编码颜色值 "${value}"`,
severity: 'error',
suggestion: `使用对应的 CSS 变量替代硬编码颜色,或新增 Token 后引用`,
currentValue: value,
});
}
}
return violations;
},
});
// 规则:检查交互状态完整性
this.rules.push({
id: 'interaction-states',
name: '交互状态完整性检查',
description: '检查交互组件是否包含必要的状态(hover、active、focus、disabled)',
severity: 'warning',
check: (component) => {
const violations: ConsistencyViolation[] = [];
const requiredStates = ['hover', 'active', 'focus', 'disabled'];
// 仅对交互类组件检查
const interactiveComponents = ['Button', 'Input', 'Select', 'Checkbox', 'Radio', 'Link'];
if (!interactiveComponents.some(name => component.name.includes(name))) {
return violations;
}
for (const state of requiredStates) {
if (!component.states.includes(state)) {
violations.push({
ruleId: 'interaction-states',
component: component.name,
filePath: component.filePath,
message: `${component.name} 缺少 "${state}" 交互状态`,
severity: 'warning',
suggestion: `为组件添加 ${state} 状态的样式定义`,
});
}
}
return violations;
},
});
// 规则:检查是否使用了已废弃的 Token
this.rules.push({
id: 'deprecated-token',
name: '废弃 Token 引用检测',
description: '检测组件是否引用了标记为 deprecated 的 Token',
severity: 'error',
check: (component, tokens) => {
const violations: ConsistencyViolation[] = [];
const deprecatedTokens = tokens.filter(t => t.deprecated);
for (const token of deprecatedTokens) {
if (component.usedTokens.includes(token.name)) {
violations.push({
ruleId: 'deprecated-token',
component: component.name,
filePath: component.filePath,
message: `${component.name} 引用了已废弃的 Token "${token.name}"`,
severity: 'error',
suggestion: '迁移至替代 Token 或联系设计团队确认',
affectedToken: token.name,
});
}
}
return violations;
},
});
}
/**
* AI 语义一致性检查
* 使用 LLM 对比组件的实际渲染效果与设计定义的语义
*/
private async aiConsistencyCheck(components: ComponentInfo[]): Promise<ConsistencyViolation[]> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000);
try {
const response = await fetch(this.llmEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
messages: [{
role: 'user',
content: JSON.stringify({
task: 'component_consistency_audit',
components: components.map(c => ({
name: c.name,
states: c.states,
tokens: c.usedTokens,
})),
instructions: [
'检查组件命名是否与设计系统规范一致',
'识别可能存在语义混淆的组件(如不同命名但功能相同的组件)',
'发现 Token 使用不当的情况(如使用 spacing token 作为颜色值)',
],
}),
}],
temperature: 0.1,
response_format: { type: 'json_object' },
}),
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`AI 审查服务异常: ${response.status}`);
}
const data = await response.json();
return (data.violations || []) as ConsistencyViolation[];
} finally {
clearTimeout(timeoutId);
}
}
}
四、CI/CD 集成:将审查自动化
将一致性审查作为 CI 流水线的一个门禁步骤:
# .github/workflows/design-consistency.yml
name: 设计系统一致性审查
on:
pull_request:
paths:
– 'src/components/**'
– 'src/tokens/**'
– 'src/styles/**'
jobs:
consistency-check:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v4
– name: 安装依赖
run: npm ci
– name: 运行一致性审查
run: npx design-audit –strict
env:
FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }}
AI_AUDIT_ENDPOINT: ${{ secrets.AI_AUDIT_ENDPOINT }}
– name: 生成审查报告
if: always()
run: npx design-audit –report –output design-audit-report.json
– name: PR 评论中展示报告
if: always()
uses: actions/github-script@v7
with:
script: |
const report = require('./design-audit-report.json');
const body = generateReportComment(report);
github.rest.issues.createComment({
…context.repo,
issue_number: context.issue.number,
body
});
五、效果度量
一致性审查的价值需要量化:
建议将一致性得分作为团队技术看板的固定指标,按周跟踪趋势。当得分连续两周下降时,触发设计系统专项治理。
总结
AI 驱动的设计系统迭代将"规范文档"转变为"可执行的自动化约束":
设计系统的价值不在于"一次性创建",而在于"持续治理"。自动化工具让治理成本从"每 PR 15 分钟的人工审核"降低到"秒级的自动检查",使团队能够将注意力从"是否合规"转向"如何更好地表达"。
