设计系统 Figma 到代码的自动化同步:Token 桥梁设计

📅 2026/7/11 5:14:37 👁️ 阅读次数 📝 编程学习
设计系统 Figma 到代码的自动化同步:Token 桥梁设计

设计系统 Figma 到代码的自动化同步:Token 桥梁设计

一、设计走查的最后一公里:从视觉稿到生产代码的断层

设计系统落地的最大痛点不在于组件实现本身,而在于设计资产与代码实现的同步维护。设计师在 Figma 中更新了主色值,前端工程师需要在代码中手动同步修改多处样式变量。这种人工同步方式不仅效率低下,还容易遗漏,导致设计稿与线上产品出现偏差。

设计 Token 是实现设计资产自动化同步的核心概念。它将设计决策(颜色、间距、字体、阴影等)抽象为平台无关的元数据,通过构建流水线自动转换为各端的代码产物。

一个完整的设计到代码自动化同步链路包含三个关键环节:Figma 插件导出 Token 数据、Node.js 构建脚本处理 Token 转换、多端代码生成器输出对应平台的样式文件。

graph LR A[Figma 设计文件] -->|Figma API / Plugin| B[Design Tokens<br/>JSON] B --> C[Token 转换引擎<br/>Style Dictionary] C --> D1[CSS 自定义属性] C --> D2[SCSS 变量] C --> D3[JavaScript 模块] C --> D4[Android resources] C --> D5[iOS Assets] D1 --> E[前端组件库] D2 --> E D3 --> E

二、Token 数据模型设计与 Figma 插件开发

2.1 设计 Token 的标准化结构

基于 W3C Design Tokens Community Group 的规范,Token 数据应采用分层结构,支持引用、别名和类型系统:

// tokens/brand.json - 品牌层 Token { "brand": { "color": { "primary": { "$value": "#1677ff", "$type": "color", "$description": "品牌主色,用于主要操作按钮和链接" }, "success": { "$value": "#52c41a", "$type": "color", "$description": "成功状态色" }, "warning": { "$value": "#faad14", "$type": "color" }, "error": { "$value": "#ff4d4f", "$type": "color" } }, "font": { "family": { "base": { "$value": "Inter, -apple-system, sans-serif", "$type": "fontFamily" } }, "size": { "sm": { "$value": "12px", "$type": "fontSize" }, "base": { "$value": "14px", "$type": "fontSize" }, "lg": { "$value": "16px", "$type": "fontSize" }, "xl": { "$value": "20px", "$type": "fontSize" } } }, "spacing": { "xs": { "$value": "4px", "$type": "dimension" }, "sm": { "$value": "8px", "$type": "dimension" }, "md": { "$value": "16px", "$type": "dimension" }, "lg": { "$value": "24px", "$type": "dimension" } } } }

2.2 Figma 插件:从设计稿提取 Token

Figma 插件通过 Plugin API 读取设计文件的样式属性,并导出为标准 Token 格式。

实际场景:处理 Figma 中已被弃用但未清理的样式

在大型设计文件中,设计师可能创建过大量测试用的废弃样式。这些样式名称混乱、颜色值不合理,如果不加过滤直接导出为 Token,会导致后续代码生成混乱。插件提取时应加入"样式有效性过滤":

/** * 样式有效性过滤器 * 排除废弃样式,确保导出的 Token 干净可用 */ function filterValidStyles(styles, rules = {}) { const defaultRules = { minNameLength: 3, // 样式名最少3个字符 excludePrefixes: ['temp', 'test', 'draft', 'deprecated', 'old'], requireDescription: false, // 是否强制要求描述 maxStylesPerCategory: 50 // 每个类别最多导出50个样式 }; const config = { ...defaultRules, ...rules }; return styles.filter(style => { const name = style.name.toLowerCase(); // 1. 排除临时样式(通过名称前缀判断) if (config.excludePrefixes.some(prefix => name.startsWith(prefix))) { console.log(`跳过废弃样式: ${style.name}`); return false; } // 2. 排除名称过短的样式 if (style.name.length < config.minNameLength) { console.log(`跳过名称过短的样式: ${style.name}`); return false; } return true; }); }

踩坑:Figma 插件异步环境下的竞态条件

在插件中遍历大量样式时,Figm Plugin API 的某些方法(如figma.getLocalPaintStyles())是同步的,但从样式对象中读取颜色值(style.paints)在某些版本中可能触发异步计算。当处理 200+ 个样式时,这会导致部分 Token 数据丢失或错位。解决方案是使用分批处理(每批 20 个)配合figma.loadAllPagesAsync()或在 manifest 中启用"documentAccess": "dynamic-page"

/** * 分批处理样式,避免竞态条件 */ async function extractStylesInBatches(styles, batchSize = 20) { const results = []; for (let i = 0; i < styles.length; i += batchSize) { const batch = styles.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(style => processStyleWithRetry(style, 2)) ); results.push(...batchResults); } return results; } async function processStyleWithRetry(style, retries) { for (let attempt = 0; attempt < retries; attempt++) { try { return await extractStyleToken(style); } catch (error) { if (attempt === retries - 1) throw error; await new Promise(r => setTimeout(r, 100)); } } }

三、Token 转换引擎:Style Dictionary 的工程化配置

Amazon 开源的 Style Dictionary 是处理 Design Token 转换的主流工具。通过自定义转换器和格式化器,可以精确控制各端代码的生成规则。

场景:设计 Token 版本管理与回退

当设计团队对 Token 做了重大变更(如品牌色调整),但没有在 Figma 中创建独立分支时,Token 构建流程需要支持版本比较。通过比较新旧 Token 的 JSON 差异,可以在构建失败或设计师审批不通过时快速回退到上一个版本的 Token:

/** * Token 版本管理器 * 支持 Token 变更的 diff 查看和回退 */ async function compareTokenVersions(oldTokens, newTokens) { const diff = { added: [], removed: [], changed: [] }; function flattenTokens(tokens, prefix = '') { const result = {}; for (const [key, value] of Object.entries(tokens)) { const path = prefix ? `${prefix}.${key}` : key; if (value && typeof value === 'object' && '$value' in value) { result[path] = value.$value; } else if (value && typeof value === 'object') { Object.assign(result, flattenTokens(value, path)); } } return result; } const oldFlat = flattenTokens(oldTokens); const newFlat = flattenTokens(newTokens); // 检测新增 for (const [key, value] of Object.entries(newFlat)) { if (!(key in oldFlat)) { diff.added.push({ key, newValue: value }); } else if (oldFlat[key] !== value) { diff.changed.push({ key, oldValue: oldFlat[key], newValue: value }); } } // 检测删除 for (const key of Object.keys(oldFlat)) { if (!(key in newFlat)) { diff.removed.push({ key, oldValue: oldFlat[key] }); } } return diff; }

3.1 基础配置文件

figma.showUI(html, { width: 360, height: 480 });

// 监听 UI 端的导出请求
figma.ui.onmessage = async (msg) => {
if (msg.type === 'export-tokens') {
try {
const tokens = await extractDesignTokens();
figma.ui.postMessage({
type: 'export-complete',
tokens: JSON.stringify(tokens, null, 2)
});
} catch (error) {
figma.ui.postMessage({
type: 'export-error',
error: error.message
});
}
}
};

/**

  • 从 Figma 文件提取设计 Token
  • 遍历本地样式(颜色、文字、间距、效果)
    */
    async function extractDesignTokens() {
    const tokens = {
    color: {},
    font: { family: {}, size: {}, weight: {}, lineHeight: {} },
    spacing: {},
    shadow: {},
    border: {}
    };

// 1. 提取颜色样式
const paintStyles = figma.getLocalPaintStyles();
for (const style of paintStyles) {
const paint = style.paints[0];
if (paint.type === 'SOLID') {
const hex = rgbToHex(paint.color);
const path = style.name.split('/'); // 如 "brand/primary"

setNestedValue(tokens.color, path, { $value: hex, $type: 'color', $description: style.description || '' }); }

}

// 2. 提取文字样式
const textStyles = figma.getLocalTextStyles();
for (const style of textStyles) {
const { fontSize, fontFamily, fontWeight, lineHeight } = style.fontSize;

setNestedValue(tokens.font.size, [style.name, 'value'], { $value: `${fontSize}px`, $type: 'fontSize' }); if (fontFamily) { setNestedValue(tokens.font.family, [style.name, 'value'], { $value: fontFamily, $type: 'fontFamily' }); }

}

// 3. 提取间距(通过 auto layout 属性推断)
// 实际实现中需遍历选中组件的 spacing 属性

return { brand: tokens };
}

/**

  • RGB 颜色转十六进制
    */
    function rgbToHex(color) {
    const r = Math.round(color.r * 255);
    const g = Math.round(color.g * 255);
    const b = Math.round(color.b * 255);
    return#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)};
    }

/**

  • 设置嵌套对象值
    */
    function setNestedValue(obj, path, value) {
    let current = obj;
    for (let i = 0; i < path.length - 1; i++) {
    if (!current[path[i]]) {
    current[path[i]] = {};
    }
    current = current[path[i]];
    }
    current[path[path.length - 1]] = value;
    }
## 三、Token 转换引擎:Style Dictionary 的工程化配置 Amazon 开源的 Style Dictionary 是处理 Design Token 转换的主流工具。通过自定义转换器和格式化器,可以精确控制各端代码的生成规则。 ### 3.1 基础配置文件 ```javascript /** * build-tokens.config.js - Style Dictionary 配置 * 定义 Token 输入、转换规则和输出目标 */ import StyleDictionary from 'style-dictionary'; import { formats, transforms } from 'style-dictionary/enums'; // 自定义转换:将 color 值转为 CSS 变量引用格式 StyleDictionary.registerTransform({ name: 'color/css-var', type: 'value', matcher: (token) => token.type === 'color', transformer: (token) => { // 如果是引用其他 Token,转为 var() 格式 if (token.value.startsWith('{')) { const refPath = token.value.replace(/[{}]/g, '').replace(/\./g, '-'); return `var(--${refPath})`; } return token.value; } }); // 自定义格式化器:生成 TypeScript 类型安全的 Token 模块 StyleDictionary.registerFormat({ name: 'typescript/module', formatter: ({ dictionary }) => { const tokens = dictionary.allTokens .map(token => { const name = token.name.replace(/\./g, '_').toUpperCase(); return ` ${name}: '${token.value}' as const`; }) .join(',\n'); return `/** * 自动生成的设计 Token 模块 * 请勿手动修改此文件 * 生成时间: ${new Date().toISOString()} */ export const designTokens = { ${tokens} } as const; export type DesignTokenKey = keyof typeof designTokens; export type DesignTokenValue = typeof designTokens[DesignTokenKey]; `; } }); // 构建配置 const sd = new StyleDictionary({ // Token 源文件 source: [ 'tokens/brand.json', 'tokens/semantic.json', 'tokens/component.json' ], // 转换规则 transformGroup: 'web', transforms: ['color/css-var', ...transforms.transformGroup.web], // 输出平台配置 platforms: { // CSS 自定义属性 css: { transformGroup: 'web', prefix: '--', buildPath: 'dist/css/', files: [ { destination: '_variables.css', format: 'css/variables', options: { outputReferences: true // 保留 Token 之间的引用关系 } } ] }, // SCSS 变量 scss: { transformGroup: 'scss', buildPath: 'dist/scss/', files: [ { destination: '_variables.scss', format: 'scss/variables', options: { outputReferences: true } } ] }, // JavaScript/TypeScript 模块 js: { transformGroup: 'js', buildPath: 'dist/js/', files: [ { destination: 'tokens.js', format: 'javascript/module', options: { outputReferences: true } }, { destination: 'tokens.d.ts', format: 'typescript/module' } ] }, // 组件库主题文件 'component-lib': { transformGroup: 'web', buildPath: 'dist/theme/', files: [ { destination: 'index.ts', format: 'typescript/module', // 仅导出组件主题相关 Token filter: (token) => token.path[0] === 'component' } ] } } }); // 执行构建 await sd.buildAllPlatforms();

3.2 语义化 Token 层设计

在设计系统中,Token 应分为三个层次:品牌层(Brand)、语义层(Semantic)、组件层(Component)。这种分层方式使得设计变更只需修改品牌层,语义层和组件层自动继承。

// tokens/semantic.json - 语义层 Token(引用品牌层) { "semantic": { "color": { "text": { "primary": { "$value": "{brand.color.neutral.900}", "$type": "color", "$description": "主要文本颜色" }, "secondary": { "$value": "{brand.color.neutral.600}", "$type": "color" }, "disabled": { "$value": "{brand.color.neutral.400}", "$type": "color" } }, "background": { "primary": { "$value": "{brand.color.neutral.0}", "$type": "color" }, "secondary": { "$value": "{brand.color.neutral.50}", "$type": "color" } }, "action": { "primary": { "$value": "{brand.color.primary}", "$type": "color" }, "danger": { "$value": "{brand.color.error}", "$type": "color" } } } } }

四、CI/CD 集成:设计变更的自动化发布流水线

将 Token 同步流程集成到 CI/CD 流水线中,实现设计变更的自动检测、转换和发布:

# .github/workflows/design-token-sync.yml name: Design Token Sync on: # Figma webhook 触发(需配置 Figma plugin 调用 webhook) repository_dispatch: types: [figma-token-update] # 手动触发 workflow_dispatch: # Token 源文件变更时触发 push: paths: - 'tokens/**' - 'scripts/build-tokens.js' jobs: build-and-publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 获取完整历史用于版本比对 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: npm ci - name: Build design tokens run: npm run tokens:build env: FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }} FIGMA_FILE_ID: ${{ secrets.FIGMA_FILE_ID }} - name: Check for changes id: git-check run: | if [[ -z $(git status --porcelain) ]]; then echo "changed=false" >> $GITHUB_OUTPUT else echo "changed=true" >> $GITHUB_OUTPUT fi - name: Commit and push changes if: steps.git-check.outputs.changed == 'true' run: | git config user.name "Design Token Bot" git config user.email "design-token-bot@example.com" git add dist/ git commit -m "chore: auto-sync design tokens from Figma [skip ci]" git push - name: Version bump and publish if: steps.git-check.outputs.changed == 'true' run: | npm version patch --no-git-tag-version npm run build npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create Pull Request for review if: steps.git-check.outputs.changed == 'true' uses: peter-evans/create-pull-request@v5 with: title: 'chore: 设计 Token 自动同步' body: | ## 自动生成的设计 Token 更新 - Figma 文件变更已自动同步 - 请 Review 生成的样式文件 - 确认无误后合并此 PR branch: auto/design-token-sync

构建脚本:Figma API 集成

/** * scripts/sync-figma-tokens.js * 通过 Figma API 获取设计文件中的样式,并导出为 Token 文件 */ import fetch from 'node-fetch'; import fs from 'fs/promises'; import path from 'path'; const FIGMA_ACCESS_TOKEN = process.env.FIGMA_ACCESS_TOKEN; const FIGMA_FILE_ID = process.env.FIGMA_FILE_ID; async function syncTokensFromFigma() { if (!FIGMA_ACCESS_TOKEN || !FIGMA_FILE_ID) { throw new Error('缺少 Figma 访问凭证,请检查环境变量'); } try { // 1. 获取 Figma 文件样式 const response = await fetch( `https://api.figma.com/v1/files/${FIGMA_FILE_ID}/styles`, { headers: { 'X-Figma-Token': FIGMA_ACCESS_TOKEN } } ); if (!response.ok) { throw new Error(`Figma API 请求失败: ${response.status} ${response.statusText}`); } const data = await response.json(); // 2. 转换为标准 Token 格式 const tokens = transformFigmaStylesToTokens(data.meta.styles); // 3. 写入 Token 文件 const outputPath = path.join(process.cwd(), 'tokens', 'brand.json'); await fs.mkdir(path.dirname(outputPath), { recursive: true }); await fs.writeFile( outputPath, JSON.stringify(tokens, null, 2), 'utf-8' ); console.log(`✅ Token 已同步到 ${outputPath}`); return tokens; } catch (error) { console.error('❌ Figma Token 同步失败:', error.message); throw error; } } /** * 将 Figma 样式转换为 Design Token 格式 */ function transformFigmaStylesToTokens(styles) { const tokens = { brand: { color: {} } }; for (const style of styles) { if (style.style_type === 'FILL') { // 构建嵌套路径(Figma 样式名使用 "/" 分隔) const pathParts = style.name.split('/'); let current = tokens.brand.color; for (let i = 0; i < pathParts.length - 1; i++) { if (!current[pathParts[i]]) { current[pathParts[i]] = {}; } current = current[pathParts[i]]; } current[pathParts[pathParts.length - 1]] = { $value: style.node_id, // 实际颜色值需通过额外 API 获取 $type: 'color', $description: style.description || '' }; } } return tokens; } // 执行同步 syncTokensFromFigma().catch(error => { process.exit(1); });

五、总结

设计系统 Figma 到代码的自动化同步通过 Design Token 桥梁实现了设计资产与工程代码的单向数据流。核心实施路径包括:在 Figma 中建立规范的样式命名体系、通过插件或 API 导出标准 Token 数据、使用 Style Dictionary 转换并生成多端代码、在 CI/CD 中建立自动发布机制。

落地路线:定义 Token 分层规范 → 配置 Figma 样式命名约定 → 搭建 Token 转换流水线 → 集成到组件库构建流程 → 建立设计变更的自动通知机制。