Figma-to-React 自律ワークフロー は何をしますか?
概要
このスキルは、Figmaデザインを本番環境対応のReactアプリケーションに変換するための完全なパイプラインを統括します。Figmaからデザインシステム(カラー、タイポグラフィ、スペーシング、エフェクト)を抽出し、Tailwind CSS設定とCSSカスタムプロパティに変換した後、ソースデザインを忠実に再現する機能的なTypeScript Reactコンポーネントを生成します。
ワークフローは4つのフェーズで動作します:インタラクティブなディスカバリー、TDDスキャフォールド、自律実行、検証済みデリバリー。結果として、Reactコンポーネント、ページ、およびデザイントークンシステムが生成され、Next.js、Vite、Remixプロジェクトに統合可能で、Figmaソースに対するピクセルレベルの正確性が検証されます。
中核となる原則:
- TDDは必須 — テストはコンポーネントより先に作成され、後から作成することはありません
- すべてのコンポーネントは明示的なプロップインターフェースを持つ関数型TypeScriptコンポーネントです
- スタイリングはすべて、Figmaのデザイントークンに基づくTailwindユーティリティクラスを使用します
- ハードコードされたカラー値、フォントサイズ、スペーシングは使用せず、すべてトークンシステムを参照します
- 画像は適切なインポートまたはpublicディレクトリ参照を使用し、パスが壊れることはありません
- 出力はフレームワークを認識します(Next.js App Router、Vite SPA、Remixルート)
- ピクセル完全一致検証 — 手動比較ではなく、pixelmatchによる自動差分ループを使用します
- アプリタイプ認識 — Chrome拡張機能、PWA、Webアプリにそれぞれ適切なテスト戦略を提供します
使用するタイミング
以下の場合にこのスキルを使用します:
- FigmaデザインファイルをReactアプリケーションに変換する場合
- Figmaからデザインシステムを抽出してTailwind設定にする場合
- Figmaのコンポーネントセットからコンポーネントライブラリを生成する場合
- Figmaのリデザインから既存UIを再構築する場合
- Figmaプロトタイプから新しいReactプロジェクトを開始する場合
トリガーフレーズ:
- 「このFigmaデザインをReactに変換して」
- 「FigmaからReactコンポーネントを生成して」
- 「Figmaからデザイントークンを抽出して」
- 「このFigmaファイルからReactアプリをビルドして」
- 「Figma to Next.js」
- 「Figma to Vite」
フェーズ1: ディスカバリー(インタラクティブ)
このフェーズではユーザー入力が必要です。ユーザーが計画を確認するまで自律的に進めないでください。
パイプライン統合チェック
質問をする前に、上流のアーティファクトが存在するか確認します:
1. 確認: .claude/plans/build-spec.json
→ 存在する場合: すべてのディスカバリー質問をスキップ。specを直接ロードします。
→ ログ: 「figma-intakeからbuild-spec.jsonを発見 — ディスカバリーをスキップ」
2. 確認: src/styles/design-tokens.lock.json
→ 存在する場合: フェーズ2でのトークン抽出をスキップ。ロックファイルの値を使用します。
→ ログ: 「design-tokens.lock.jsonを発見 — ロックされたトークン値を使用」
build-spec.jsonが存在する場合、specのフレームワーク、コンポーネント、およびオプションをプリロードして、フェーズ2に直接ジャンプします。
ステップ 1.1: Figmaコンテキストの収集
Figma MCPを使用してデザインファイルを検査します。
1. get_metadata — ファイル名、ページ、最終更新日、コンポーネント数
2. get_variable_defs — デザイントークン(カラー、タイポグラフィ、スペーシング、エフェクト)
3. get_design_context — レイアウト構造、コンポーネント階層、オートレイアウト設定
4. get_screenshot — 各ページ/フレームのビジュアルリファレンス
ステップ 1.2: デザイントークンの抽出
Figma変数とスタイルをトークン構造にマッピングします:
デザイントークン
├── カラー
│ ├── プリミティブ (blue-500、gray-100など)
│ ├── セマンティック (primary、secondary、destructive、muted)
│ └── コンポーネント固有 (card-bg、button-primary、input-border)
├── タイポグラフィ
│ ├── フォントファミリー(フォールバックスタック付き)
│ ├── フォントサイズ(xsから5xlのスケール)
│ ├── フォントウェイト
│ ├── 行の高さ
│ └── 文字間隔
├── スペーシング
│ ├── ベースユニット
│ └── スケール (0.5から96)
├── ボーダー半径
│ ├── スケール (sm、md、lg、xl、full)
│ └── コンポーネント固有のオーバーライド
├── シャドウ/エフェクト
│ ├── ボックスシャドウ (sm、md、lg、xl)
│ └── ドロップシャドウ
└── ブレークポイント
└── モバイル、タブレット、デスクトップ、ワイド
ステップ 1.3: コンポーネントの調査
すべてのFigmaコンポーネントを特定し、Reactコンポーネントにマッピングします:
| Figmaコンポーネント | Reactコンポーネント | カテゴリ |
|---|---|---|
| Button/Primary | <Button variant="primary"> | プリミティブ |
| Card | <Card> | コンテナ |
| Navigation/Header | <Header> | レイアウト |
| Hero Section | <HeroSection> | セクション |
| Input/Text | <Input type="text"> | フォーム |
ステップ 1.4: 出力ターゲットの確認
ユーザーに次の確認を求めます:
- フレームワーク: Next.js (App Router)、Next.js (Pages Router)、Vite、またはRemix
- スタイリングアプローチ: Tailwind CSS(デフォルト)、CSS Modules、またはstyled-components
- コンポーネントライブラリベース: shadcn/ui、Radix UI、Headless UI、またはなし
- 状態管理: React state、Zustand、Jotai、またはなし
- 出力ディレクトリ: 例えば
src/components/、app/等
コンポーネントインベントリとトークンサマリーを、続行する前にユーザーの承認を得るために提示します。
フェーズ2: 実行(自律)
ユーザーがディスカバリー計画を承認したら、すべてのステップを自律的に進めます。
トークンロックファイル制約
src/styles/design-tokens.lock.jsonが存在する場合、生成されるすべてのコードはロックファイルの値を参照する必要があります:
- カラー: ロックファイルにマッピングされたTailwindクラスを使用し、近似のhex値は使用しない
- タイポグラフィ: ロックファイルの正確なフォントファミリー、サイズ、ウェイトを使用
- スペーシング: ロックファイルのスペーシングスケール値を使用
- テキストコンテンツ:
lockfile.textContentの正確な文字列を使用
ロックファイルが存在する場合、値を近似したり推測したりしないでください。 値がロックファイルにない場合は、最初にロックファイルに追加してから参照します。
テストファーストゲート(必須)
TDDは強制されます。 テストファイルが存在するまで、コンポーネントの実装を開始してはなりません。これは提案ではなく、ハードゲートです。
コンポーネント実装を書く前に:
1. 実行: ./scripts/verify-test-coverage.sh
→ 終了コード1の場合: 停止。最初にtdd-from-figmaスキルを呼び出します。
→ 終了コード0の場合: 実装に進みます。
2. 生成する各コンポーネントに対して:
a. 必須: src/components/**/{ComponentName}.test.tsxが存在すること
→ 存在しない場合: 停止。最初にテストを書きます(tdd-from-figmaを呼び出す)。
→ これはオプションではありません。事前に存在するテストなしではコンポーネントは出荷されません。
b. テストファイルを読み、期待される動作を理解する
c. 実装はすべてのテストに合格する必要があります
d. コンポーネント作成後、実行: pnpm vitest run {test-file}
e. テストが失敗した場合: テストではなくコンポーネントを修正します
3. バッチ内のすべてのコンポーネントを書き終えた後:
→ 実行: pnpm vitest run --reporter=verbose
→ すべてのテストに合格する(グリーンフェーズ)まで進めない
→ 失敗がある場合: グリーンになるまでコンポーネントを修正。フェーズ3に進まない。
4. テストカバレッジがしきい値を満たしていることを確認:
→ 実行: pnpm vitest run --coverage
→ 必須: pipeline.config.json → tdd.coverageThreshold (デフォルト: 80%)
このゲートが存在する理由: テストはロックファイルからの正確な値(テキストコンテンツ、ARIAロール、コンポーネント構造)をエンコードします。事前に書かれたテストがなければ、実装がデザインと一致することを検証するメカニズムがありません。テストはFigmaデザインとReactコード間の契約です。
ステップ 2.1: Tailwind設定の生成
抽出されたトークンでtailwind.config.tsを作成または拡張します:
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
content: ["./src/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
// Figma変数からマッピングされたセマンティックトークン
primary: {
DEFAULT: "var(--color-primary)",
foreground: "var(--color-primary-foreground)",
// ... フルスケール
},
// ... すべてのカラートークン
},
fontFamily: {
sans: ["var(--font-sans)", "system-ui", "sans-serif"],
heading: ["var(--font-heading)", "system-ui", "sans-serif"],
},
fontSize: {
// Figmaのタイポグラフィスタイルからマッピング
},
spacing: {
// Figmaのスペーシング変数からマッピング
},
borderRadius: {
// Figmaの角丸トークンからマッピング
},
boxShadow: {
// Figmaのエフェクトスタイルからマッピング
},
},
},
plugins: [],
};
export default config;
カスタムプロパティを持つCSSファイルを生成します:
/* src/styles/tokens.css */
:root {
/* カラー - プリミティブ */
--color-blue-500: #3b82f6;
/* ... */
/* カラー - セマンティック */
--color-primary: var(--color-blue-500);
--color-primary-foreground: #ffffff;
/* ... */
/* タイポグラフィ */
--font-sans: "Inter", system-ui, sans-serif;
--font-heading: "Plus Jakarta Sans", system-ui, sans-serif;
/* ... */
}
ステップ 2.2: Reactコンポーネントの生成
フェーズ1で特定された各コンポーネントに対して、以下を生成します:
コンポーネントごとのファイル構造:
src/components/
├── ui/
│ ├── button.tsx # プリミティブコンポーネント
│ ├── input.tsx
│ ├── card.tsx
│ └── ...
├── layout/
│ ├── header.tsx # レイアウトコンポーネント
│ ├── footer.tsx
│ └── ...
├── sections/
│ ├── hero-section.tsx # ページセクション
│ ├── features-grid.tsx
│ └── ...
└── pages/ # フルページ構成(またはNext.jsの場合はapp/)
├── home-page.tsx
└── ...
コンポーネントテンプレート:
// src/components/ui/button.tsx
import { type ButtonHTMLAttributes, forwardRef } from "react";
import { cn } from "@/lib/utils";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "outline" | "ghost";
size?: "sm" | "md" | "lg";
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = "primary", size = "md", ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
"inline-flex items-center justify-center rounded-md font-medium transition-colors",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
"disabled:pointer-events-none disabled:opacity-50",
{
"bg-primary text-primary-foreground hover:bg-primary/90": variant === "primary",
"bg-secondary text-secondary-foreground hover:bg-secondary/90": variant === "secondary",
"border border-input bg-transparent hover:bg-accent": variant === "outline",
"hover:bg-accent hover:text-accent-foreground": variant === "ghost",
},
{
"h-8 px-3 text-sm": size === "sm",
"h-10 px-4 text-sm": size === "md",
"h-12 px-6 text-base": size === "lg",
},
className
)}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, type ButtonProps };
コンポーネント生成のルール:
- すべてのコンポーネントは、適切な箇所で
forwardRefを使用した関数型コンポーネントです - すべてのコンポーネントに明示的なTypeScript
interfaceがプロップにあります - すべてのスタイリングは、トークン値を参照するTailwindクラスを使用します
- コンポーネントは合成のために
classNameプロップを受け入れます - クラスマージには
cn()ユーティリティ(clsx + tailwind-merge)を使用します - インタラクティブ要素には適切なARIA属性を含めます
- コンポーネントとそのプロップタイプの両方をエクスポートします
ステップ 2.3: ページ構成の生成
セクションコンポーネントを、Figmaフレームに一致するフルページに構成します:
// app/page.tsx (Next.js App Router)
import { Header } from "@/components/layout/header";
import { HeroSection } from "@/components/sections/hero-section";
import { FeaturesGrid } from "@/components/sections/features-grid";
import { Footer } from "@/components/layout/footer";
export default function HomePage() {
return (
<div className="flex min-h-screen flex-col">
<Header />
<main className="flex-1">
<HeroSection />
<FeaturesGrid />
</main>
<Footer />
</div>
);
}
ステップ 2.4: 画像とアセットの処理
Figmaからの静的画像の場合:
// Next.js - next/imageを使用
import Image from "next/image";
import heroImage from "@/assets/images/hero.webp";
<Image src={heroImage} alt="ヒーローイラスト" width={1200} height={600} priority />
// Vite - 標準インポートを使用
import heroImage from "@/assets/images/hero.webp";
<img src={heroImage} alt="ヒーローイラスト" width={1200} height={600} loading="eager" />
ルール:
- 画像はFigmaから1xと2xでエクスポートし、Retina対応にする
- WebP形式をJPEG/PNGフォールバックと共に使用する
public/images/またはsrc/assets/images/に配置する- すべての
<img>には意味のあるalt属性が必要 - ヒーロー/ファーストビュー画像には
priority(Next.js)またはloading="eager"を使用 - ファーストビュー以下の画像には
loading="lazy"を使用
ステップ 2.5: ユーティリティファイルの生成
src/lib/
├── utils.ts # cn()ヘルパー、フォーマッター
└── fonts.ts # フォント読み込み(Next.js: next/font、Vite: @fontsource)
ステップ 2.6: テストの実行(テストファイルが存在する場合)
すべてのコンポーネントを生成した後、tdd-from-figmaによってテストファイルが作成された場合:
pnpm vitest run --reporter=verbose
テストが失敗した場合: テストの期待値に一致するようにコンポーネントの実装を修正します。テストは信頼できるものです — テストはロックファイルからの正確な値をエンコードしています。実装に一致するようにテストを変更しないでください。
すべてのテストに合格するまで繰り返します(TDDのグリーンフェーズ)。
ステップ 2.7: Storybookストーリーの生成(非ブロッキング)
コンポーネントがビルドされ、ユニットテストに合格したら、StorybookストーリーとMDXドキュメントを自動生成します。storybook-story-generationスキルを呼び出すか(またはスクリプトを直接実行します)。これはパイプラインのフェーズ4.5であり、非ブロッキングです — ここでの失敗は警告をログに記録し、パイプラインは続行します。
# すべてのコンポーネントに対して.stories.tsx + .mdxを生成(既存のストーリーはスキップ)
./scripts/generate-stories.sh
# 書き込まずにプレビュー、または強制再生成
…
[翻訳のため省略]