Figma Developer について
Figma Developerは、Figmaのデザインをコードに変換する手順をまとめたスキルです。デザイントークンやコンポーネント構造の抽出、コンポーネント生成、同期ワークフローの自動化を扱います。
主な機能
- 01アクセストークンでFigmaに接続
- 02デザイントークンの抽出
- 03トークンをコードとして出力
- 04コンポーネントの抽出と生成
- 05デザイン同期ワークフローの自動化
活用シーン
- 01Figmaデザインからコード用トークンを作成
- 02FigmaファイルからUIコンポーネントを生成
- 03デザイントークンとコンポーネントを同期
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ daffy0208/ ai-dev-standards --skill figma-developerインストール後、Claude Code で/figma-developerを入力して読み込まれたか確認します。
同梱ファイルmanifest.yaml
詳細
Figma 開発者
Figma のデザインを、本番環境で使えるコードに変換します。
基本原則
デザインを唯一の信頼できる情報源とします。
デザイナーは Figma で作業し、開発者は Figma をもとに構築します。その間をつなぐ作業は、手作業ではなく自動化するべきです。
フェーズ 1:セットアップと認証
Figma アクセストークンの取得
- Figma Settings に移動します
- 「Personal access tokens」までスクロールします
- 「Generate new token」をクリックします
- 名前を付けます(例:「Development」)
- コピーして安全に保存します
環境のセットアップ
# .env
FIGMA_ACCESS_TOKEN=figd_...
Figma クライアントのインストール
npm install node-fetch
接続のテスト
import { FigmaClient } from '@/integrations/design-tools/figma/client'
const client = new FigmaClient({
accessToken: process.env.FIGMA_ACCESS_TOKEN
})
// Test with a public file
const file = await client.getFile('abc123xyz')
console.log('Connected! File:', file.name)
フェーズ 2:デザイントークンの抽出
デザイントークンとは?
デザイントークンとは、コードとして保存されたデザイン上の決定事項(色、タイポグラフィ、スペーシング)です。
メリット:
- 唯一の信頼できる情報源
- プラットフォーム間で一貫性を保てる
- 更新が簡単
- 型安全
Figma からトークンを抽出する
// scripts/sync-design-tokens.ts
import { FigmaClient } from '@/integrations/design-tools/figma/client'
import fs from 'fs/promises'
async function syncDesignTokens() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
console.log('Extracting design tokens...')
// Extract tokens
const tokens = await client.extractDesignTokens(fileKey)
console.log(`Found:`)
console.log(` ${tokens.colors.length} colors`)
console.log(` ${tokens.typography.length} text styles`)
console.log(` ${tokens.spacing.length} spacing values`)
// Export as CSS
const css = await client.exportTokensAsCSS(fileKey)
await fs.writeFile('src/styles/design-tokens.css', css)
// Export as JSON
const json = await client.exportTokensAsJSON(fileKey)
await fs.writeFile('src/styles/design-tokens.json', json)
console.log('Design tokens synced!')
}
syncDesignTokens()
コードでトークンを使用する
// src/styles/design-tokens.css
:root {
/* Colors */
--color-primary: #0066cc;
--color-secondary: #10b981;
--color-neutral-100: #f9fafb;
--color-neutral-900: #111827;
/* Typography */
--font-heading-family: Inter;
--font-heading-size: 48px;
--font-heading-weight: 700;
/* Spacing */
--space-4: 16px;
--space-8: 32px;
}
React での使用例:
// components/Button.tsx
export function Button({ children }: { children: React.ReactNode }) {
return (
<button
style={{
backgroundColor: 'var(--color-primary)',
color: 'white',
padding: 'var(--space-4)',
fontFamily: 'var(--font-heading-family)',
fontWeight: 'var(--font-heading-weight)',
border: 'none',
borderRadius: '8px',
cursor: 'pointer'
}}
>
{children}
</button>
)
}
フェーズ 3:アセットのエクスポート
アイコンを SVG としてエクスポートする
// scripts/export-icons.ts
import { FigmaClient } from '@/integrations/design-tools/figma/client'
import fs from 'fs/promises'
async function exportIcons() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
// Get file
const file = await client.getFile(fileKey)
// Find "Icons" frame
const iconsFrame = findNode(file.document, 'Icons')
if (!iconsFrame || !iconsFrame.children) {
throw new Error('Icons frame not found')
}
console.log(`Found ${iconsFrame.children.length} icons`)
// Export as SVG
const iconIds = iconsFrame.children.map(child => child.id)
const svgs = await client.exportImages(fileKey, iconIds, {
format: 'svg'
})
// Save each SVG
for (const svg of svgs) {
const response = await fetch(svg.url)
const content = await response.text()
await fs.writeFile(`public/icons/${svg.name}.svg`, content)
console.log(` ✓ ${svg.name}.svg`)
}
console.log('Icons exported!')
}
function findNode(node: any, name: string): any {
if (node.name === name) return node
if (node.children) {
for (const child of node.children) {
const found = findNode(child, name)
if (found) return found
}
}
return null
}
exportIcons()
React アイコンコンポーネントを生成する
// scripts/generate-icon-components.ts
import { FigmaClient } from '@/integrations/design-tools/figma/client'
import fs from 'fs/promises'
async function generateIconComponents() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
const file = await client.getFile(fileKey)
const iconsFrame = findNode(file.document, 'Icons')
if (!iconsFrame || !iconsFrame.children) {
throw new Error('Icons frame not found')
}
// Export icons
const iconIds = iconsFrame.children.map(child => child.id)
const svgs = await client.exportImages(fileKey, iconIds, {
format: 'svg'
})
// Generate React components
for (const svg of svgs) {
const response = await fetch(svg.url)
const svgContent = await response.text()
// Convert to React component
const componentName = toPascalCase(svg.name)
const component = `
import React from 'react'
export function ${componentName}Icon(props: React.SVGProps<SVGSVGElement>) {
return (
${svgContent.replace('<svg', '<svg {...props}')}
)
}
`.trim()
await fs.writeFile(`components/icons/${componentName}Icon.tsx`, component)
console.log(` ✓ ${componentName}Icon.tsx`)
}
// Generate index file
const indexContent = svgs
.map(svg => {
const componentName = toPascalCase(svg.name)
return `export { ${componentName}Icon } from './${componentName}Icon'`
})
.join('\n')
await fs.writeFile('components/icons/index.ts', indexContent)
console.log('Icon components generated!')
}
function toPascalCase(str: string): string {
return str
.split(/[-_\s]+/)
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('')
}
function findNode(node: any, name: string): any {
if (node.name === name) return node
if (node.children) {
for (const child of node.children) {
const found = findNode(child, name)
if (found) return found
}
}
return null
}
generateIconComponents()
使用例:
import { HomeIcon, UserIcon, SettingsIcon } from '@/components/icons'
export function Navigation() {
return (
<nav>
<HomeIcon width={24} height={24} />
<UserIcon width={24} height={24} />
<SettingsIcon width={24} height={24} />
</nav>
)
}
フェーズ 4:コンポーネントの生成
コンポーネント構造を抽出する
// scripts/extract-components.ts
import { FigmaClient } from '@/integrations/design-tools/figma/client'
async function extractComponents() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
// Get components
const components = await client.getFileComponents(fileKey)
console.log('Components:')
for (const [key, component] of Object.entries(components)) {
console.log(` ${component.name}`)
console.log(` Key: ${component.key}`)
console.log(` Description: ${component.description}`)
}
// Get component sets (variants)
const componentSets = await client.getComponentSets(fileKey)
console.log('\nComponent Sets:')
for (const [setId, variants] of Object.entries(componentSets)) {
console.log(` Set: ${setId}`)
for (const variant of variants) {
console.log(` - ${variant.name}`)
}
}
}
extractComponents()
Figma からボタンコンポーネントを生成する
// scripts/generate-button.ts
import { FigmaClient } from '@/integrations/design-tools/figma/client'
import fs from 'fs/promises'
async function generateButtonComponent() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
// Get button component
const components = await client.getFileComponents(fileKey)
const buttonComponent = Object.values(components).find(c =>
c.name.toLowerCase().includes('button')
)
if (!buttonComponent) {
throw new Error('Button component not found')
}
// Get component node
const file = await client.getFile(fileKey)
const buttonNode = findNodeById(file.document, buttonComponent.key)
if (!buttonNode) {
throw new Error('Button node not found')
}
// Extract styles
const styles = extractStyles(buttonNode)
// Generate React component
const component = `
import React from 'react'
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost'
size?: 'sm' | 'md' | 'lg'
children: React.ReactNode
onClick?: () => void
}
export function Button({
variant = 'primary',
size = 'md',
children,
onClick
}: ButtonProps) {
const baseStyles = {
fontFamily: '${styles.fontFamily}',
fontWeight: ${styles.fontWeight},
fontSize: '${styles.fontSize}px',
padding: '${styles.padding}',
borderRadius: '${styles.borderRadius}px',
border: 'none',
cursor: 'pointer',
transition: 'all 0.2s'
}
const variantStyles = {
primary: {
backgroundColor: '${styles.backgroundColor}',
color: '${styles.color}'
},
secondary: {
backgroundColor: 'transparent',
color: '${styles.backgroundColor}',
border: '2px solid ${styles.backgroundColor}'
},
ghost: {
backgroundColor: 'transparent',
color: '${styles.color}'
}
}
return (
<button
style={{ ...baseStyles, ...variantStyles[variant] }}
onClick={onClick}
>
{children}
</button>
)
}
`.trim()
await fs.writeFile('components/Button.tsx', component)
console.log('Button component generated!')
}
function findNodeById(node: any, id: string): any {
if (node.id === id) return node
if (node.children) {
for (const child of node.children) {
const found = findNodeById(child, id)
if (found) return found
}
}
return null
}
function extractStyles(node: any) {
return {
fontFamily: node.style?.fontFamily || 'Inter',
fontWeight: node.style?.fontWeight || 600,
fontSize: node.style?.fontSize || 16,
padding: '12px 24px',
borderRadius: node.cornerRadius || 8,
backgroundColor: rgbToHex(node.fills?.[0]?.color || { r: 0, g: 0.4, b: 0.8 }),
color: '#ffffff'
}
}
function rgbToHex(color: any): string {
const r = Math.round(color.r * 255)
.toString(16)
.padStart(2, '0')
const g = Math.round(color.g * 255)
.toString(16)
.padStart(2, '0')
const b = Math.round(color.b * 255)
.toString(16)
.padStart(2, '0')
return `#${r}${g}${b}`
}
generateButtonComponent()
フェーズ 5:自動化されたワークフロー
GitHub Actions を設定する
# .github/workflows/sync-figma.yml
name: Sync Figma Design Tokens
on:
schedule:
- cron: '0 9 * * *' # Every day at 9am
workflow_dispatch: # Manual trigger
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm install
- name: Sync design tokens
env:
FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }}
run: npm run sync:design-tokens
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
title: 'chore: sync design tokens from Figma'
body: 'Automated sync of design tokens from Figma'
branch: 'figma/sync-tokens'
commit-message: 'chore: sync design tokens'
Package.json スクリプト
{
"scripts": {
"sync:design-tokens": "tsx scripts/sync-design-tokens.ts",
"export:icons": "tsx scripts/export-icons.ts",
"generate:icons": "tsx scripts/generate-icon-components.ts",
"figma:sync-all": "npm run sync:design-tokens && npm run generate:icons"
}
}
ベストプラクティス
1. Figma ファイルを整理する
構成:
Design System File
├── 📄 Cover (description)
├── 🎨 Colors (all color styles)
├── 📝 Typography (all text styles)
├── 📏 Spacing (spacing guide)
├── 🧩 Components
│ ├── Buttons
│ ├── Forms
│ └── Cards
└── 🖼️ Icons (all icons in one frame)
2. 命名規則
色:
Primary/500
Secondary/500
Neutral/100
Neutral/900
Success
Error
タイポグラフィ:
Heading/Large
Heading/Medium
Body/Regular
Body/Small
コンポーネント:
Button/Primary
Button/Secondary
Card/Default
Card/Elevated
3. Figma Variables(ベータ版)を使用する
Figma は現在、変数をネイティブでサポートしています。次の項目に使用します。
- 色
- スペーシング
- 角丸
- タイポグラフィのサイズ
API を使って、これらを自動的に抽出します。
4. バージョン管理
// Check for Figma updates
async function checkForUpdates() {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
const file = await client.getFile(fileKey)
const currentVersion = file.version
// Store in database or file
const previousVersion = await getPreviousVersion()
if (currentVersion !== previousVersion) {
console.log('Figma file updated!')
console.log(`Version: ${previousVersion} → ${currentVersion}`)
// Trigger sync
await syncDesignTokens()
await savePreviousVersion(currentVersion)
} else {
console.log('No updates')
}
}
よくあるパターン
パターン 1:トークンベースの開発
// 1. Extract tokens
const tokens = await client.extractDesignTokens(fileKey)
// 2. Generate CSS variables
const css = generateCSS(tokens)
// 3. Generate TypeScript types
const types = `
export type ColorToken =
${tokens.colors.map(c => `| '${c.name}'`).join('\n ')}
export type SpacingToken =
${tokens.spacing.map(s => `| '${s.name}'`).join('\n ')}
`.trim()
await fs.writeFile('src/types/tokens.ts', types)
// 4. Use in components
import { ColorToken } from '@/types/tokens'
interface ButtonProps {
color: ColorToken
}
パターン 2:コンポーネントの同期
// Keep components in sync with Figma
async function syncComponent(componentName: string) {
const client = new FigmaClient()
const fileKey = 'YOUR_FIGMA_FILE_KEY'
// Get component from Figma
const components = await client.getFileComponents(fileKey)
const component = Object.values(components).find(c => c.name === componentName)
if (!component) {
throw new Error(`Component not found: ${componentName}`)
}
// Generate code
const code = await generateComponentCode(component)
// Write to file
await fs.writeFile(`components/${componentName}.tsx`, code)
console.log(`Synced: ${componentName}`)
}
トラブルシューティング
問題:トークン名が一致しない
問題: Figma のスタイル名にスペースや特殊文字が含まれている
解決策: 名前を正規化する
function normalizeTokenName(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
}
問題:色が異なって見える
問題: RGB 値の変換が必要
解決策: 適切な色空間変換を使用する
function rgbToHex(color: { r: number; g: number; b: number }): string {
const r = Math.round(color.r * 255)
const g = Math.round(color.g * 255)
const b = Math.round(color.b * 255)
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`
}
問題:API のレート制限
問題: リクエストが多すぎる
解決策: レスポンスをキャッシュする
const cache = new Map<string, { data: any; timestamp: number }>()
async function getCachedFile(fileKey: string) {
const cached = cache.get(fileKey)
if (cached && Date.now() - cached.timestamp < 60000) {
return cached.data
}
const file = await client.getFile(fileKey)
cache.set(fileKey, { data: file, timestamp: Date.now() })
return file
}
ツールとリソース
Figma プラグイン:
- Figma Tokens - デザイントークンを管理
- Design Tokens - トークンをエクスポート
- Figma to Code - コードを生成
ライブラリ:
@figma/rest-api-spec- TypeScript の型figma-api- 代替クライアントstyle-dictionary- トークンを変換
関連スキル:
design-system-architect- デザインシステムの構築visual-designer- デザイン原則asset-manager- エクスポートしたアセットの管理
デザインを自動的にコードに変換します。 🎨→💻
コメント
Markdown 対応