FigmaAPI集成与设计自动化Skill figma

这个技能用于通过Figma API提取设计令牌、生成React/CSS代码、同步设计系统、自动化设计到代码工作流,并支持Figma插件开发和Dev模式集成。关键词包括Figma API、设计令牌、代码生成、前端开发、自动化工作流、React组件、Tailwind CSS、设计系统。

前端开发 0 次安装 0 次浏览 更新于 3/22/2026

名称: figma 描述: 集成Figma API进行设计自动化和代码生成。用于提取设计令牌、从Figma组件生成React/CSS代码、同步设计系统、构建Figma插件或自动化设计到代码工作流。触发条件包括Figma API、设计令牌、Figma插件、设计到代码、Figma导出、Figma组件、开发模式。

Figma API 集成

通过Figma API提取设计数据、从组件生成代码并自动化设计工作流。

快速开始

身份验证

const FIGMA_TOKEN = process.env.FIGMA_TOKEN;

const headers = {
  'X-Figma-Token': FIGMA_TOKEN
};

// 获取文件
const response = await fetch(
  `https://api.figma.com/v1/files/${FILE_KEY}`,
  { headers }
);

文件密钥和节点ID

// 从Figma URL提取: figma.com/file/FILE_KEY/Name?node-id=NODE_ID
const figmaUrl = 'https://www.figma.com/file/abc123/MyDesign?node-id=1%3A2';
const fileKey = figmaUrl.match(/file\/([^/]+)/)?.[1];  // abc123
const nodeId = new URL(figmaUrl).searchParams.get('node-id');  // 1:2

核心API端点

获取文件

// 完整文件
GET https://api.figma.com/v1/files/:file_key

// 特定节点(组件)
GET https://api.figma.com/v1/files/:file_key/nodes?ids=1:2,1:3

// 带几何数据以获取SVG路径
GET https://api.figma.com/v1/files/:file_key?geometry=paths

// 带插件数据
GET https://api.figma.com/v1/files/:file_key?plugin_data=shared

获取组件

// 获取文件中所有组件
GET https://api.figma.com/v1/files/:file_key/components

// 获取组件集(变体)
GET https://api.figma.com/v1/files/:file_key/component_sets

// 获取团队发布的组件
GET https://api.figma.com/v1/teams/:team_id/components

导出图像

// 将节点导出为图像
GET https://api.figma.com/v1/images/:file_key?ids=1:2,1:3&format=png&scale=2

// 导出为SVG
GET https://api.figma.com/v1/images/:file_key?ids=1:2&format=svg

// 响应
{
  "images": {
    "1:2": "https://s3.amazonaws.com/...",
    "1:3": "https://s3.amazonaws.com/..."
  }
}

组件代码生成

Figma节点到React组件

interface FigmaNode {
  id: string;
  name: string;
  type: string;
  children?: FigmaNode[];
  absoluteBoundingBox?: { x: number; y: number; width: number; height: number };
  fills?: Fill[];
  strokes?: Stroke[];
  effects?: Effect[];
  cornerRadius?: number;
  paddingLeft?: number;
  paddingRight?: number;
  paddingTop?: number;
  paddingBottom?: number;
  itemSpacing?: number;
  layoutMode?: 'HORIZONTAL' | 'VERTICAL' | 'NONE';
  primaryAxisAlignItems?: string;
  counterAxisAlignItems?: string;
  characters?: string;
  style?: TextStyle;
}

async function getComponentCode(fileKey: string, nodeId: string): Promise<string> {
  const response = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${nodeId}`,
    { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
  );
  const data = await response.json();
  const node = data.nodes[nodeId].document;
  
  return generateReactComponent(node);
}

function generateReactComponent(node: FigmaNode): string {
  const componentName = toPascalCase(node.name);
  const styles = extractStyles(node);
  const children = node.children?.map(child => generateJSX(child)).join('
') || '';
  
  return `
import React from 'react';

export function ${componentName}() {
  return (
    <div style={${JSON.stringify(styles, null, 2)}}>
      ${children}
    </div>
  );
}
`;
}

function extractStyles(node: FigmaNode): React.CSSProperties {
  const styles: React.CSSProperties = {};
  
  // 尺寸
  if (node.absoluteBoundingBox) {
    styles.width = node.absoluteBoundingBox.width;
    styles.height = node.absoluteBoundingBox.height;
  }
  
  // 背景
  if (node.fills?.length) {
    const fill = node.fills.find(f => f.visible !== false && f.type === 'SOLID');
    if (fill?.color) {
      styles.backgroundColor = rgbaToHex(fill.color);
    }
  }
  
  // 边框半径
  if (node.cornerRadius) {
    styles.borderRadius = node.cornerRadius;
  }
  
  // 内边距
  if (node.paddingLeft) styles.paddingLeft = node.paddingLeft;
  if (node.paddingRight) styles.paddingRight = node.paddingRight;
  if (node.paddingTop) styles.paddingTop = node.paddingTop;
  if (node.paddingBottom) styles.paddingBottom = node.paddingBottom;
  
  // 弹性盒布局(自动布局)
  if (node.layoutMode && node.layoutMode !== 'NONE') {
    styles.display = 'flex';
    styles.flexDirection = node.layoutMode === 'HORIZONTAL' ? 'row' : 'column';
    styles.gap = node.itemSpacing;
    
    // 对齐方式
    const alignMap: Record<string, string> = {
      'MIN': 'flex-start',
      'CENTER': 'center',
      'MAX': 'flex-end',
      'SPACE_BETWEEN': 'space-between',
    };
    if (node.primaryAxisAlignItems) {
      styles.justifyContent = alignMap[node.primaryAxisAlignItems] || 'flex-start';
    }
    if (node.counterAxisAlignItems) {
      styles.alignItems = alignMap[node.counterAxisAlignItems] || 'flex-start';
    }
  }
  
  return styles;
}

function generateJSX(node: FigmaNode): string {
  const styles = extractStyles(node);
  
  if (node.type === 'TEXT') {
    return `<span style={${JSON.stringify(styles)}}>${node.characters || ''}</span>`;
  }
  
  if (node.type === 'VECTOR' || node.type === 'BOOLEAN_OPERATION') {
    return `{/* Vector: ${node.name} - 导出为 SVG */}`;
  }
  
  const children = node.children?.map(child => generateJSX(child)).join('
') || '';
  return `<div style={${JSON.stringify(styles)}}>${children}</div>`;
}

从Figma生成Tailwind CSS

function figmaToTailwind(node: FigmaNode): string {
  const classes: string[] = [];
  
  // 尺寸
  if (node.absoluteBoundingBox) {
    const { width, height } = node.absoluteBoundingBox;
    classes.push(`w-[${Math.round(width)}px]`, `h-[${Math.round(height)}px]`);
  }
  
  // 背景颜色
  if (node.fills?.length) {
    const fill = node.fills.find(f => f.visible !== false && f.type === 'SOLID');
    if (fill?.color) {
      const hex = rgbaToHex(fill.color);
      classes.push(`bg-[${hex}]`);
    }
  }
  
  // 边框半径
  if (node.cornerRadius) {
    const radiusMap: Record<number, string> = {
      4: 'rounded-sm', 6: 'rounded-md', 8: 'rounded-lg',
      12: 'rounded-xl', 16: 'rounded-2xl', 9999: 'rounded-full'
    };
    classes.push(radiusMap[node.cornerRadius] || `rounded-[${node.cornerRadius}px]`);
  }
  
  // 内边距
  if (node.paddingLeft === node.paddingRight && 
      node.paddingTop === node.paddingBottom &&
      node.paddingLeft === node.paddingTop) {
    classes.push(`p-[${node.paddingLeft}px]`);
  } else {
    if (node.paddingLeft) classes.push(`pl-[${node.paddingLeft}px]`);
    if (node.paddingRight) classes.push(`pr-[${node.paddingRight}px]`);
    if (node.paddingTop) classes.push(`pt-[${node.paddingTop}px]`);
    if (node.paddingBottom) classes.push(`pb-[${node.paddingBottom}px]`);
  }
  
  // 弹性盒布局
  if (node.layoutMode && node.layoutMode !== 'NONE') {
    classes.push('flex');
    classes.push(node.layoutMode === 'HORIZONTAL' ? 'flex-row' : 'flex-col');
    if (node.itemSpacing) classes.push(`gap-[${node.itemSpacing}px]`);
    
    const justifyMap: Record<string, string> = {
      'MIN': 'justify-start', 'CENTER': 'justify-center',
      'MAX': 'justify-end', 'SPACE_BETWEEN': 'justify-between'
    };
    const alignMap: Record<string, string> = {
      'MIN': 'items-start', 'CENTER': 'items-center', 'MAX': 'items-end'
    };
    if (node.primaryAxisAlignItems) classes.push(justifyMap[node.primaryAxisAlignItems]);
    if (node.counterAxisAlignItems) classes.push(alignMap[node.counterAxisAlignItems]);
  }
  
  return classes.join(' ');
}

从文件提取所有组件

interface ComponentInfo {
  key: string;
  name: string;
  description: string;
  nodeId: string;
  thumbnailUrl?: string;
}

async function getAllComponents(fileKey: string): Promise<ComponentInfo[]> {
  const response = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/components`,
    { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
  );
  const data = await response.json();
  
  return data.meta.components.map((comp: any) => ({
    key: comp.key,
    name: comp.name,
    description: comp.description || '',
    nodeId: comp.node_id,
    thumbnailUrl: comp.thumbnail_url,
  }));
}

// 为所有组件生成代码
async function generateAllComponentsCode(fileKey: string): Promise<Map<string, string>> {
  const components = await getAllComponents(fileKey);
  const codeMap = new Map<string, string>();
  
  for (const comp of components) {
    const code = await getComponentCode(fileKey, comp.nodeId);
    codeMap.set(comp.name, code);
  }
  
  return codeMap;
}

设计令牌提取

提取颜色

async function extractColors(fileKey: string) {
  const file = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/styles`,
    { headers }
  ).then(r => r.json());
  
  const colors = {};
  
  for (const style of file.meta.styles) {
    if (style.style_type === 'FILL') {
      const nodeData = await getStyleNode(fileKey, style.node_id);
      const fill = nodeData.fills[0];
      
      if (fill.type === 'SOLID') {
        colors[style.name] = rgbaToHex(fill.color);
      }
    }
  }
  
  return colors;
}

function rgbaToHex({ r, g, b, a = 1 }) {
  const toHex = (n) => Math.round(n * 255).toString(16).padStart(2, '0');
  return `#${toHex(r)}${toHex(g)}${toHex(b)}${a < 1 ? toHex(a) : ''}`;
}

提取排版令牌

async function extractTypography(fileKey: string) {
  const response = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/styles`,
    { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
  );
  const data = await response.json();
  
  const typography: Record<string, any> = {};
  
  for (const style of data.meta.styles) {
    if (style.style_type === 'TEXT') {
      const nodeResponse = await fetch(
        `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${style.node_id}`,
        { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
      );
      const nodeData = await nodeResponse.json();
      const node = nodeData.nodes[style.node_id].document;
      
      typography[style.name] = {
        fontFamily: node.style?.fontFamily,
        fontWeight: node.style?.fontWeight,
        fontSize: node.style?.fontSize,
        lineHeight: node.style?.lineHeightPx,
        letterSpacing: node.style?.letterSpacing,
      };
    }
  }
  
  return typography;
}

生成完整设计令牌

interface DesignTokens {
  colors: Record<string, string>;
  typography: Record<string, any>;
  spacing: Record<string, number>;
  shadows: Record<string, string>;
  radii: Record<string, number>;
}

async function generateDesignTokens(fileKey: string): Promise<DesignTokens> {
  const [colors, typography] = await Promise.all([
    extractColors(fileKey),
    extractTypography(fileKey),
  ]);
  
  return {
    colors,
    typography,
    spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 },  // 从文件提取
    shadows: {},  // 从效果样式提取
    radii: {},    // 从节点提取
  };
}

// 生成CSS变量
function tokensToCSSVariables(tokens: DesignTokens): string {
  let css = ':root {
';
  
  // 颜色
  for (const [name, value] of Object.entries(tokens.colors)) {
    const cssName = name.toLowerCase().replace(/[\s/]+/g, '-');
    css += `  --color-${cssName}: ${value};
`;
  }
  
  // 排版
  for (const [name, style] of Object.entries(tokens.typography)) {
    const cssName = name.toLowerCase().replace(/[\s/]+/g, '-');
    css += `  --font-${cssName}-family: "${style.fontFamily}";
`;
    css += `  --font-${cssName}-size: ${style.fontSize}px;
`;
    css += `  --font-${cssName}-weight: ${style.fontWeight};
`;
    css += `  --font-${cssName}-line-height: ${style.lineHeight}px;
`;
  }
  
  css += '}
';
  return css;
}

// 生成Tailwind配置
function tokensToTailwindConfig(tokens: DesignTokens): string {
  const colors: Record<string, string> = {};
  for (const [name, value] of Object.entries(tokens.colors)) {
    const key = name.toLowerCase().replace(/[\s/]+/g, '-');
    colors[key] = value;
  }
  
  return `
module.exports = {
  theme: {
    extend: {
      colors: ${JSON.stringify(colors, null, 6)},
      fontFamily: {
        ${Object.entries(tokens.typography).map(([name, style]) => 
          `'${name.toLowerCase()}': ['${style.fontFamily}', 'sans-serif']`
        ).join(',
        ')}
      },
    },
  },
};
`;
}

变量API(设计令牌2.0)

// 获取变量(需要企业/组织计划)
GET https://api.figma.com/v1/files/:file_key/variables/local

// 获取变量集合
GET https://api.figma.com/v1/files/:file_key/variables/local/collections

// 响应结构
interface VariableCollection {
  id: string;
  name: string;
  modes: { modeId: string; name: string }[];
  variableIds: string[];
}

interface Variable {
  id: string;
  name: string;
  resolvedType: 'BOOLEAN' | 'FLOAT' | 'STRING' | 'COLOR';
  valuesByMode: Record<string, any>;
}

async function getVariables(fileKey: string) {
  const response = await fetch(
    `https://api.figma.com/v1/files/${fileKey}/variables/local`,
    { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
  );
  return response.json();
}

开发模式集成

从开发模式获取代码片段

// 获取开发资源(注释、测量)
GET https://api.figma.com/v1/files/:file_key/dev_resources

// 创建开发资源
POST https://api.figma.com/v1/dev_resources
{
  "dev_resource": {
    "name": "React组件",
    "url": "https://github.com/...",
    "file_key": "abc123",
    "node_id": "1:2"
  }
}

Webhooks

设置Webhook

POST https://api.figma.com/v2/webhooks

{
  "event_type": "FILE_UPDATE",
  "team_id": "123456",
  "endpoint": "https://your-server.com/figma-webhook",
  "passcode": "your-secret-passcode"
}

// 可用事件:
// - FILE_UPDATE
// - FILE_DELETE  
// - FILE_VERSION_UPDATE
// - LIBRARY_PUBLISH
// - FILE_COMMENT

处理Webhook

app.post('/figma-webhook', async (req, res) => {
  const { passcode } = req.body;
  
  if (passcode !== process.env.FIGMA_WEBHOOK_SECRET) {
    return res.status(401).send('未授权');
  }
  
  const { event_type, file_key, timestamp } = req.body;
  
  switch (event_type) {
    case 'FILE_UPDATE':
      await syncDesignTokens(file_key);
      break;
    case 'LIBRARY_PUBLISH':
      await regenerateComponents(file_key);
      break;
    case 'FILE_COMMENT':
      await notifyTeam(req.body);
      break;
  }
  
  res.status(200).send('OK');
});

完整设计到代码流水线

// 完整流水线: Figma文件 → React组件 + 令牌
async function figmaToCode(fileKey: string, outputDir: string) {
  // 1. 获取所有组件
  const components = await getAllComponents(fileKey);
  
  // 2. 生成设计令牌
  const tokens = await generateDesignTokens(fileKey);
  await fs.writeFile(
    `${outputDir}/tokens.css`,
    tokensToCSSVariables(tokens)
  );
  await fs.writeFile(
    `${outputDir}/tailwind.config.js`,
    tokensToTailwindConfig(tokens)
  );
  
  // 3. 生成React组件
  for (const comp of components) {
    const code = await getComponentCode(fileKey, comp.nodeId);
    const fileName = toPascalCase(comp.name) + '.tsx';
    await fs.writeFile(`${outputDir}/components/${fileName}`, code);
  }
  
  // 4. 将图标导出为SVG
  const icons = components.filter(c => c.name.startsWith('Icon/'));
  if (icons.length) {
    const iconIds = icons.map(i => i.nodeId).join(',');
    const svgResponse = await fetch(
      `https://api.figma.com/v1/images/${fileKey}?ids=${iconIds}&format=svg`,
      { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
    );
    const svgData = await svgResponse.json();
    
    for (const icon of icons) {
      const svgUrl = svgData.images[icon.nodeId];
      const svg = await fetch(svgUrl).then(r => r.text());
      await fs.writeFile(`${outputDir}/icons/${icon.name}.svg`, svg);
    }
  }
  
  console.log(`生成了${components.length}个组件和${Object.keys(tokens.colors).length}个颜色令牌`);
}

Figma插件开发

插件清单 (manifest.json)

{
  "name": "我的Figma插件",
  "id": "123456789",
  "api": "1.0.0",
  "main": "code.js",
  "ui": "ui.html",
  "editorType": ["figma", "figjam"],
  "capabilities": ["codegen"],
  "codegenLanguages": [
    { "label": "React", "value": "react" },
    { "label": "Vue", "value": "vue" }
  ]
}

插件代码 (code.ts)

// 显示UI
figma.showUI(__html__, { width: 400, height: 500 });

// 处理选择
figma.on('selectionchange', () => {
  const selection = figma.currentPage.selection;
  figma.ui.postMessage({ type: 'selection', nodes: selection.map(nodeToJSON) });
});

// 处理来自UI的消息
figma.ui.onmessage = async (msg) => {
  if (msg.type === 'export-code') {
    const node = figma.currentPage.selection[0];
    const code = generateCode(node);
    figma.ui.postMessage({ type: 'code-generated', code });
  }
};

function nodeToJSON(node: SceneNode) {
  return {
    id: node.id,
    name: node.name,
    type: node.type,
    width: node.width,
    height: node.height,
  };
}

资源