Figma到代码的自动化工作流

Figma 插件、Design-to-Code 工具与 Stitch 集成


1. 设计到代码的鸿沟

设计师在 Figma 中精心打磨的界面,到了开发者手中往往"走样"。这种鸿沟源于三个层面的信息损失:

设计意图                     代码实现
+------------------+        +------------------+
| 间距: 16px        | -----> | margin: 15px     |  (数值偏差)
| 颜色: #3B82F6    | -----> | color: #3a81f5   |  (取色误差)
| 字重: Medium     | -----> | font-weight: 500 |  (还好)
| 圆角: 12px       | -----> | border-radius: 8px| (忽略)
| 动效: 300ms ease | -----> | (未实现)          |  (遗漏)
+------------------+        +------------------+

1.1 信息损失的三个层面

层面 损失类型 典型症状
令牌层 设计值未编码为变量 开发者硬编码随机值
组件层 组件变体/状态不完整 开发者漏实现某些状态
交互层 动效/手势未文档化 界面"能用但不灵动"

1.2 自动化的目标

理想工作流:
Figma 设计
    |
    v
自动提取 (Token + 组件 + 交互)
    |
    v
代码生成 (框架组件 + 样式 + 动画)
    |
    v
人工精调 (业务逻辑 + 数据绑定)
    |
    v
发布

2. Figma 插件生态

2.1 核心工具链

工具 功能 定位
Figma Tokens Studio Design Token 管理 Token 层
Figma Dev Mode 开发者视图/标注 交接层
Figma REST API 程序化访问设计文件 自动化基础
Anima Figma 转 React/Vue/HTML 组件层
Locofy Figma 转 Next.js/React 组件层
Builder.io (Figma) Figma 转任意框架 组件层
Relay (Google) Figma 转 Jetpack Compose 移动端

2.2 Figma Tokens Studio 工作流

在 Figma 中:
+----------------------------------+
| Tokens Studio 插件面板            |
|                                  |
| 颜色:                            |
|   primary: #3B82F6              |
|   secondary: #7C3AED            |
|                                  |
| 字体:                            |
|   heading: 24px / 700           |
|   body: 16px / 400              |
|                                  |
| 间距:                            |
|   sm: 8px                       |
|   md: 16px                      |
|   lg: 24px                      |
|                                  |
| [同步到 GitHub]                  |
+----------------------------------+
    |
    v (Push to GitHub)
tokens.json (Git Repository)
    |
    v (CI: Style Dictionary)
CSS / Tailwind / TS

配置同步到 GitHub:

{
  "name": "design-tokens",
  "tokenSets": ["global", "light", "dark"],
  "storage": {
    "provider": "github",
    "repository": "org/design-system",
    "branch": "main",
    "filePath": "tokens"
  },
  "transformers": [
    {
      "platform": "css",
      "output": "dist/tokens.css"
    },
    {
      "platform": "tailwind",
      "output": "dist/tailwind.config.js"
    }
  ]
}

3. Figma REST API 自动化

3.1 API 概览

Figma API 端点:
GET /v1/files/:file_key                    # 获取文件结构
GET /v1/files/:file_key/nodes?ids=:ids     # 获取特定节点
GET /v1/images/:file_key?ids=:ids          # 导出图片
GET /v1/files/:file_key/styles             # 获取样式
GET /v1/files/:file_key/components         # 获取组件

3.2 自动提取设计数据

import requests

FIGMA_TOKEN = "figd_..."
FILE_KEY = "abcdef123"

headers = {"X-Figma-Token": FIGMA_TOKEN}

def get_figma_file(file_key):
    """获取 Figma 文件完整结构"""
    url = f"https://api.figma.com/v1/files/{file_key}"
    response = requests.get(url, headers=headers)
    return response.json()

def extract_colors(file_data):
    """从 Figma 文件提取颜色样式"""
    colors = {}
    styles = file_data.get("styles", {})

    for style_id, style_info in styles.items():
        if style_info["styleType"] == "FILL":
            # 获取样式节点详情
            node = get_node(FILE_KEY, style_id)
            fills = node.get("fills", [])
            if fills and fills[0]["type"] == "SOLID":
                color = fills[0]["color"]
                hex_color = rgb_to_hex(color["r"], color["g"], color["b"])
                colors[style_info["name"]] = hex_color

    return colors

def extract_typography(file_data):
    """从 Figma 文件提取字体样式"""
    typography = {}
    styles = file_data.get("styles", {})

    for style_id, style_info in styles.items():
        if style_info["styleType"] == "TEXT":
            node = get_node(FILE_KEY, style_id)
            style = node.get("style", {})
            typography[style_info["name"]] = {
                "fontFamily": style.get("fontFamily"),
                "fontSize": style.get("fontSize"),
                "fontWeight": style.get("fontWeight"),
                "lineHeightPx": style.get("lineHeightPx"),
                "letterSpacing": style.get("letterSpacing"),
            }

    return typography

def export_component_images(file_key, component_ids, scale=2):
    """导出组件为图片"""
    ids = ",".join(component_ids)
    url = f"https://api.figma.com/v1/images/{file_key}"
    params = {"ids": ids, "scale": scale, "format": "png"}
    response = requests.get(url, headers=headers, params=params)
    return response.json()["images"]

3.3 自动化管线

GitHub Actions Workflow:
+-------------------+     +-------------------+
| Figma Webhook     | --> | GitHub Actions    |
| (设计文件更新)     |     | (自动触发)         |
+-------------------+     +-------------------+
                                  |
                  +---------------+---------------+
                  |               |               |
                  v               v               v
          提取 Tokens       导出组件图片      生成代码
          (API + 转换)      (PNG/SVG)        (模板引擎)
                  |               |               |
                  v               v               v
          tokens.css        assets/          components/
          tailwind.config   icons/           Button.tsx
                                             Card.tsx
                  |               |               |
                  +---------------+---------------+
                                  |
                                  v
                          Pull Request
                          (自动 review)

4. Design-to-Code 工具

4.1 Anima

Anima 将 Figma 设计转换为 React/Vue/HTML 代码。

工作流程:

1. 在 Figma 中安装 Anima 插件
2. 选择要导出的 Frame
3. Anima 分析层级结构
4. 生成组件代码 (React / Vue / HTML)
5. 在 Anima Web 平台预览
6. 下载或通过 CLI 集成到项目

输出质量:
- 布局: Flexbox/Grid (80% 准确)
- 样式: CSS / Tailwind (90% 准确)
- 响应式: 需手动调整断点
- 交互: 基础状态变化 (hover/active)
- 业务逻辑: 需完全手动添加

4.2 Locofy

Locofy 专注于生成 Next.js / React 项目级代码。

Locofy 特性:
- 自动识别组件边界
- 生成路由结构 (Next.js App Router)
- 输出 Tailwind CSS 类
- 支持自定义组件映射
- 集成 Storybook

局限:
- 复杂动画不支持
- 需要 Figma 设计遵循特定规范
- 生成代码需要大量重构

4.3 Builder.io Figma 插件

Builder.io 方案:
Figma -> Visual Editor -> React / Vue / Svelte / Angular / etc.

优势:
- 支持几乎所有前端框架
- 可视化编辑器作为中间层
- 支持 A/B 测试
- CMS 内容绑定

用法:
1. Figma 中选择设计
2. "Import to Builder" 一键导入
3. 在 Builder 编辑器中调整
4. 生成目标框架代码

5. Google Stitch 集成

5.1 Stitch 在工作流中的定位

传统流程:
  需求 -> Figma 设计 (3-5天) -> 开发 (5-10天)

Stitch 加速流程:
  需求 -> Stitch 生成原型 (5分钟) -> 评审 -> 代码转换 -> 开发精调 (3-5天)

5.2 Stitch MCP 工作流

// 使用 Stitch MCP 工具链
// 1. 列出项目
const projects = await stitch.listProjects();

// 2. 获取项目详情
const project = await stitch.getProject({ projectId: "abc123" });

// 3. 列出屏幕
const screens = await stitch.listScreens({ projectId: "abc123" });

// 4. 获取屏幕详情(HTML + CSS)
const screen = await stitch.getScreen({
  projectId: "abc123",
  screenId: "screen_1"
});

// 5. 转换为框架代码
function convertStitchToReact(html, css) {
  // 解析 HTML 结构
  // 将 Tailwind CDN 类转为本地 Tailwind
  // 替换静态图片为 next/image
  // 提取可复用组件
  // 绑定 props 和事件
}

5.3 DESIGN.md 工作流

# DESIGN.md - 语义设计系统

## Colors
- primary: #2563EB (蓝色, 主操作/链接)
- secondary: #7C3AED (紫色, AI 元素)
- surface: #FFFFFF (浅色模式背景)
- text-primary: #1E293B (主文字)

## Typography
- heading: Inter 700 (24/32/40px)
- body: Inter 400 (16px, line-height 1.6)
- code: JetBrains Mono 400 (14px)

## Spacing
- base: 8px
- content-gap: 16px
- section-gap: 32px
- page-margin: 48px

## Components
- card: radius-12, shadow-sm, padding-24
- button-primary: bg-primary, text-white, radius-8, h-40
- input: border-gray-200, radius-8, h-40, focus:ring-primary

Stitch 在后续屏幕生成时引用 DESIGN.md 保持一致性。


6. 代码转换管线

6.1 HTML 到 React 转换

// 转换规则
const conversionRules = {
  // HTML 属性到 JSX
  attributeMap: {
    'class': 'className',
    'for': 'htmlFor',
    'tabindex': 'tabIndex',
    'onclick': 'onClick',
    'onchange': 'onChange',
  },

  // 自闭合标签
  selfClosing: ['img', 'br', 'hr', 'input'],

  // style 字符串到对象
  convertInlineStyle(styleStr: string): Record<string, string> {
    return Object.fromEntries(
      styleStr.split(';')
        .filter(Boolean)
        .map(s => {
          const [key, value] = s.split(':').map(p => p.trim());
          const camelKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
          return [camelKey, value];
        })
    );
  },

  // Tailwind CDN 到本地
  migrateTailwindCDN(html: string): string {
    // 移除 CDN script 标签
    // 保留 class 中的 Tailwind 类名
    // 确保 tailwind.config.js 包含所有使用的类
    return html.replace(
      /<script src="https:\/\/cdn\.tailwindcss\.com"><\/script>/,
      ''
    );
  }
};

6.2 图片资源处理

async function processImages(html: string, outputDir: string) {
  const imgRegex = /<img[^>]+src="([^"]+)"[^>]*>/g;
  let match;

  while ((match = imgRegex.exec(html)) !== null) {
    const src = match[1];

    if (src.startsWith('http')) {
      // 下载远程图片
      const filename = generateFilename(src);
      await downloadImage(src, path.join(outputDir, 'public/images', filename));

      // 替换为 next/image
      html = html.replace(
        match[0],
        `<Image src="/images/${filename}" alt="" width={800} height={600} />`
      );
    } else if (src.startsWith('data:')) {
      // Base64 图片保存为文件
      const filename = `image_${Date.now()}.png`;
      await saveBase64Image(src, path.join(outputDir, 'public/images', filename));

      html = html.replace(
        match[0],
        `<Image src="/images/${filename}" alt="" width={800} height={600} />`
      );
    }
  }

  return html;
}

7. 组件提取策略

7.1 何时提取组件

提取信号:
  1. 重复出现 2+ 次的相同结构
  2. 具有明确边界的独立功能块
  3. 存在变体(不同尺寸/颜色/状态)
  4. 未来可能复用

不提取信号:
  1. 只出现一次且不太可能复用
  2. 结构过于简单(单个元素)
  3. 提取后 props 比原始代码还复杂

7.2 提取流程

原始 HTML:
<div class="flex items-center gap-3 p-4 rounded-lg border">
  <img class="w-10 h-10 rounded-full" src="avatar.jpg" alt="User" />
  <div>
    <p class="font-medium text-gray-900">张三</p>
    <p class="text-sm text-gray-500">产品经理</p>
  </div>
  <button class="ml-auto text-blue-600">关注</button>
</div>

提取为组件:
// UserCard.tsx
interface UserCardProps {
  name: string;
  role: string;
  avatarUrl: string;
  onFollow?: () => void;
}

function UserCard({ name, role, avatarUrl, onFollow }: UserCardProps) {
  return (
    <div className="flex items-center gap-3 p-4 rounded-lg border">
      <Image
        src={avatarUrl}
        alt={name}
        width={40}
        height={40}
        className="rounded-full"
      />
      <div>
        <p className="font-medium text-text-primary">{name}</p>
        <p className="text-sm text-text-secondary">{role}</p>
      </div>
      {onFollow && (
        <button onClick={onFollow} className="ml-auto text-primary">
          关注
        </button>
      )}
    </div>
  );
}

8. CI/CD 集成

8.1 设计变更自动同步

# .github/workflows/design-sync.yml
name: Design Token Sync

on:
  push:
    paths:
      - 'tokens/**'

jobs:
  build-tokens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Build tokens
        run: npx style-dictionary build

      - name: Run visual regression
        run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}

      - name: Check contrast ratios
        run: node scripts/check-contrast.js

      - name: Create PR if tokens changed
        uses: peter-evans/create-pull-request@v5
        with:
          title: "chore: update design tokens"
          body: "Automated token sync from Figma Tokens Studio"
          branch: design-token-update

8.2 视觉回归检测

Chromatic 工作流:
1. Storybook 构建
2. 截图每个组件的每个变体
3. 与基准截图对比
4. 差异 > 阈值 -> 标记为需审核
5. 设计师/开发者审核差异
6. 接受 -> 更新基准 | 拒绝 -> 修复代码

9. 实际工作流对比

方案 自动化程度 代码质量 维护成本 适用场景
纯手写 0% 最高 复杂交互组件
Figma Dev Mode + 手写 20% 日常开发
Stitch -> 手动转换 40% 中高 新页面快速原型
Anima / Locofy 60% 市场营销页
Token Studio + CI 80% (令牌层) 设计系统维护

10. 推荐工作流

最佳实践工作流:

1. 设计阶段:
   Figma + Tokens Studio (管理 Token)
   |
   v

2. 原型验证:
   Google Stitch (快速生成高保真原型)
   |
   v

3. Token 同步:
   Tokens Studio -> GitHub -> Style Dictionary -> CSS/Tailwind
   (自动化, CI/CD)
   |
   v

4. 组件开发:
   手工编写 React/Vue 组件 (引用 Token)
   Storybook 文档化
   |
   v

5. 质量保障:
   Chromatic 视觉回归
   axe-core 无障碍检测
   |
   v

6. 持续同步:
   设计变更 -> Token 更新 -> 自动 PR -> 审核 -> 合并

核心理念:Token 自动化,组件手写。令牌层完全自动化同步,组件层由开发者手工编写以确保质量和可维护性,两者通过 Token 引用保持一致。


Maurice | maurice_wen@proton.me