🌭

Figma→実装の再現度を上げるAIワークフロー設計(Claude Code × Playwright)

に公開

はじめに

REALITY で Server エンジニアをしているあさだです。今回は Claude Code に触れる機会があり、実際に試した内容を記事としてまとめさせていただきます。

本記事では Claude Code と公式の Figma MCP を使い、Figma のデザインコンテキストから REALITY の Web フロントエンド(Vue) の実装へ落とし込むためのAIワークフローについて紹介します。

この記事で得られること

  • 大規模なデザインコンテキストをClaude Codeで扱うためのAIワークフロー設計パターン
  • Claude Code、Figma MCP、Playwright MCPを組み合わせたワークフロー構築例

対象読者

  • AIを活用した開発ワークフローに興味があるエンジニア
  • Claude CodeやMCPを使った自動化を検討している方
  • デザインからコード実装作業を効率化したい方

使用技術

技術 用途
Claude Code AIコーディングアシスタント
Figma MCP(公式) FigmaデザインコンテキストのAIへの受け渡し(Dev Mode)
Playwright MCP ブラウザ操作とレンダリング検証
Vue.js 2.7 最終的な出力フォーマット(REALITYのWebフロントエンドで採用)

用語解説

この記事で使用する主要な用語について簡単に説明します。

Claude Code とは

Claude Code は Anthropic が提供する AI コーディングアシスタントです。ターミナル上で動作し、コードの生成・編集・実行を対話的に行えます。ファイル操作やコマンド実行など、広範な操作を AI に委譲できます。

MCP とは

MCP(Model Context Protocol)は、AI が外部ツールやサービスと連携するための標準プロトコルです。Figma MCP や Playwright MCP のように、さまざまなサービス向け MCP サーバーが提供されており、AI エージェントがツールを直接操作できるようになります。

Figma MCP

Figma MCP を使うと、AI は Figma のデザインコンテキスト(レイヤー構造・スタイル・テキストなど)やスクリーンショットを取得できます。
また、デザインコンテキストは React + Tailwind CSS 形式 のコードとして出力されます。

コンテキストについて

コンテキストは、AI モデルが一度に保持できる情報量の上限です。会話履歴、読み込んだファイル、ツール出力など、すべてが容量を消費します。

上限に近づくと Compact(要約・圧縮) が走り、読み込ませたデザインコンテキストや前提条件が欠落することがあります。これが再現度を落とす典型要因になります。

Playwright とは

Playwright は Web ブラウザを自動操作するツールです。ページ遷移、クリック、スクリーンショット取得などをプログラムから実行できます。E2E テストによく使われますが、本記事では レンダリング検証 や Computed Style の抽出 にも活用します。


やりたいこと

REALITY では、Figma で作成したデザインをもとに Web フロントエンドを Vue で実装しています。作業としては、Figma の CSS を確認しながら Vue の実装へ落とし込み、必要なアセットを配置していく流れになります。

この実装プロセスを効率化するため、Figma MCP が返すデザインコンテキスト(React + Tailwind CSS 形式)を入力として、Claude Code による Vue 実装の自動化を目指しました。

想定する流れは以下の通りです。

[Figmaデータ] → [Claude Code] → [Vue]

※ Figma MCP はデザインコンテキストを React + Tailwind CSS 形式のコードとして返します。
※ 検証時は基本的に拡張思考モードを有効にしています。
※ LLMのモデルは Claude Opus 4.5 を採用しています。

検証と課題

1 つの画面を構成するデザインコンテキストには、レイヤー構造、位置情報、スタイル、テキストなど非常に多くの情報が含まれます。これらをまとめて AI に渡すと、1 回のやり取りで数万トークンを消費します。

その結果、実装を進める過程で、期待した再現度に到達せず、作業が途中で破綻するケースが頻発しました。


具体例:REALITY思い出アルバム

検証対象として「REALITY 思い出アルバム」という比較的リッチなデザインのページを題材に、Claude Code と Figma MCP でデザインコンテキストから Vue 実装を試みました。

https://x.com/reality_totsu/status/2004484833586589783

以下はデザインの一部抜粋です。
単純なフォーム画面とは異なり、手作業でも次の点で実装差分が出やすく、再現度を評価する際の重要な指標になります。

  • 余白(padding / margin)の微妙な差
  • 文字表現(フォント、ウェイト、行間)
  • 要素の整列方法(grid / flex の使い分け)
  • 重なり順(z-index)や背景レイヤー構造

結果として、この規模のデザインコンテキストを特に戦略なくAIに任せて実装させようとしても、期待する出力はほぼ得られませんでした。

Claude Code が Figma MCP のレスポンス自体を読み込めず、次のようにエラーになるケースも出ました。

Error: result (77,565 characters) exceeds maximum allowed tokens.

AIの思考ログを追うと、デザインコンテキストのサイズの大きさを理由に次のような判断が行われ、結果として一部実装が省略・置換されるケースが見られました。

  • デザインコンテキストのサイズが大きいので、作業を簡略化する

  • 後続デザインが類似しているとして、複製で対応する

  • 時間がかかりすぎているとして、残りの作業を省略する

  • 複雑な処理などの簡略化のためにスクリプト実装を始める

画面全体の再現が必要であるにもかかわらず、途中で実装方針が変わってしまい、最終的にデザインの一部が反映されない、間違ったデザインを出力し「完了」となりました。

これらの検証結果から、

  • サイズの大きいデザインコンテキストをどのようにしてAIに入力させていくか

  • AIがデザインコンテキスト通りの出力を行っていることを検証できていない

  • 毎回同じようなスクリプトの実装を行ってしまう

などが課題として考えられました。


解決策:デザインコンテキストを分割して扱うためのタスクパイプライン

この課題に対して、Figma MCPから生成されたデザインコンテキスト(React + Tailwind CSS形式)を分割し、タスクごとに必要最小限の情報だけを扱う パイプライン型のワークフローを構築しました。

基本アイデア

❌ 初期: [Figmaデータ] → [1つのAIエージェント] → [Vue]

✅ 改善: [Figmaデータ] → [キャッシュ保存/分割] → [複数の専門AIエージェント] → [Vue]

Phase構成

Phase 役割 説明
Phase 1 素材の準備 Figmaからデータを取得し、中間成果物(React)を生成・検証
Phase 2 実装と組み立て ReactからVueを実装し、最終成果物を生成・検証

全体の処理フロー

前提として、Figma MCP はデザインコンテキストを React + Tailwind CSS 形式 のコードとして返します。
本フローではこれを 「ブラウザで検証するための中間成果物」 として扱います。
まず React をブラウザで描画し、Playwright でレンダリング結果を検証します。
比較可能な「正解データ」を用意しておくことで、Vue 実装後は Playwright で差分を検出し、検証を行わせます。

これらを踏まえ、全体の流れを図解すると以下のようになります。

各ステップの役割

Step 説明
Step 1: データ取得とキャッシュ保存 Figma MCPからデータ取得し、JSONファイルにキャッシュ
Step 2: Reactコード生成と分割 Reactコードを生成し、コンポーネント単位に分割
Step 3: Reactレンダリング検証 PlaywrightでReactのレンダリングを確認
Step 4: 実装計画と依存関係解析 依存関係を解析し、Vue実装の順序を決定
Step 5: アセットコピー 画像・フォントなどのアセットをコピー
Step 6: Computed Style抽出 Playwrightで確定したCSS(Computed Style)を抽出
Step 7: Leaf実装(並列) 末端コンポーネントをVueで並列実装
Step 8: Container実装 親コンポーネントをVueで実装
Step 9: ルーティング設定 ルーティング設定を行う
Step 10: 最終検証 E2Eテスト・ビジュアル比較による最終検証

コンテキストを制御し、Figmaのデザイン精度を向上させる工夫

ここからは、フロー内で実施している「コンテキストを抑えて精度を上げる」ための工夫を紹介します。

1. 巨大レスポンスの「ファイル退避」(Step 1: データ取得とキャッシュ保存

Figma MCP のレスポンスは巨大で、そのまま入力するとコンテキストを使い切ってしまいます。さらに、デザインコンテキストが「1 行の長いデータ」として扱われ、Read/Grep での読み込み自体が不安定になることもありました。

そこで、Step 1: データ取得とキャッシュ保存 では、データ取得直後にフックを挟み、レスポンスをローカル JSON として自動保存します。デザインコンテキスト部分も整形しておくことで、必要箇所だけを安定して参照できます。

hooksの設定方法

Claude Codeのhooksは、プロジェクトの .claude/settings.json または ~/.claude/settings.json(グローバル設定)に記述します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "mcp__figma-desktop__get_design_context",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/save-figma-cache.sh"
          }
        ]
      },
      {
        "matcher": "mcp__figma-desktop__get_metadata",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/save-figma-cache.sh"
          }
        ]
      },
      {
        "matcher": "mcp__figma-desktop__get_screenshot",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/save-figma-cache.sh"
          }
        ]
      }
    ]
  }
}
設定項目 説明
PostToolUse ツール実行後に呼び出されるhookタイプ
matcher 対象となるツール名(ワイルドカード * も使用可能)
type: "command" シェルコマンドを実行
command 実行するスクリプトのパス

ワイルドカードを使って複数のツールをまとめて指定することもできます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "mcp__figma-desktop__*",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/save-figma-cache.sh"
          }
        ]
      }
    ]
  }
}

hook から呼ばれるスクリプトは標準入力でツール結果を受け取り、整形してキャッシュに保存します。
AI は以後必要なタイミングで必要な箇所だけをファイルから読みます。これによりコンテキストを抑えられます。
また、デフォルトの機能として、ツールの結果は一時ファイルに格納されますが、セッションを変更したりした際でも再取得しやすく、APIのレートリミット対策などにもなります。

2. デザインコンテキストの分割と依存グラフの作成

Figma MCPが返すデザインコンテキストは、各ノードが入れ子構造になっており、子要素がネストされた形でコード化されています。

そこで、まずデザインコンテキストをコンポーネント単位に分割し、1タスクあたりで扱うコンテキストを抑える方針を採用しました。
また、親子関係の依存グラフを生成し、 末端(Leaf)→ 親(Container) の順に実装させます。

各タスクでは必要最小限のコンテキストだけを扱えるため、再現度が上がります。

当初は分割や依存関係整理も AI に任せていましたが、コンポーネントの大きさとして、分割深度や 1 ファイルあたり行数などの基準を制御するため、専用ユーティリティとして事前実装させています。

実測データ(思い出アルバムの場合):

分類 数 処理方法
Leafコンポーネント 33 先に並列実装
Containerコンポーネント 25 Leaves完了後に実装
合計 58 12バッチで処理完了

3. サブエージェントによる分業

1 つのエージェントが最初から最後まで担当すると、入力を減らしても累積でコンテキストが膨らみます。
そこで親エージェントは、個別タスクをサブエージェントに委譲し、最小限の結果だけを受け取る形にしました。

また、サブエージェントを run_in_background: true で起動しても、親が TaskOutput により、サブエージェントの実行内容を読み込んでしまいコンテキストを消費します。
これを避けるため、タスクを起動したら親は一度処理を終え、次のユーザー入力待ちにするような指示にしました。
ユーザー入力待ちでも、完了通知で再開できるため、 TaskOutput でサブエージェントの実行結果の取り込みを回避できます。

加えて、あらゆる指示の後に TodoWrite でチェックリスト更新を徹底することで、指示の抜けや無視が減りました。

以下は、メインエージェントが各ステップの処理をサブエージェントに移譲するためのプロンプトの一部の抜粋になります。

## Workflow Checklist

Copy this checklist and track your progress:

Conversion Progress:
- [ ] Step 1: データ取得とキャッシュ保存
- [ ] Step 2: Reactコード生成と分割
- [ ] Step 3: Reactレンダリング検証
- [ ] Step 4: 実装計画と依存関係解析
- [ ] Step 5: アセットコピー
- [ ] Step 6: Computed Style抽出
- [ ] Step 7: Leaf実装(並列)
- [ ] Step 8: Container実装
- [ ] Step 9: ルーティング設定
- [ ] Step 10: 最終検証

## Execution Model

タスクは1つずつ順次実行。完了通知後に次へ進む。
# メインエージェントが生成するサブエージェント起動例
Task:
  subagent_type: general-purpose
  run_in_background: true
  prompt: |
    ## 実行前チェック
    以下を順番に実行すること:

    Startup:
    - [ ] agent-rules.md をReadツールで読んだことを確認
    - [ ] {step}.md をReadツールで読んだことを確認
    - [ ] TodoWriteで進捗管理を開始したことを確認

    ## ファイルパス
    - Rules: {projectPath}/.claude/skills/figma-to-vue/reference/agent-rules.md
    - Task: {projectPath}/.claude/skills/figma-to-vue/reference/tasks/{step}.md

    ## Context
    - ProjectPath: {projectPath}
    - Input: /tmp/claude-figma-results/{session}/{prev}.json

ステップ間で必要最低限の情報のみ受け取るようにプロンプトを調整させています。

4. コンポーネント実装のバッチ並列処理

依存グラフにより、独立したコンポーネントは安全に並列実装できます。サブエージェントを組み合わせることで、大量コンポーネントでも安定して処理できました。
また、作業の簡略化や、類似作業を複製・省略するなどの動きは見られなくなっていました。

Step 7: Leaf実装(並列)
...
Step 8: Container実装
...
合計: 12バッチで58コンポーネント実装完了

5. ユーティリティスクリプトの事前準備

AI に単純な作業のくり返しをさせたり、複雑な処理のスクリプト化をさせていると、コンテキスト消費が増えるうえ結果もブレやすくなります。そこで、よく行っている処理や作業内容が確定しているものなどは専用ユーティリティを用意し、AI には実行だけさせる方針にしました。
例として以下のようなスクリプトを用意させています。

スクリプト 役割
svg-import-converter.js Figmaの仮想SVGデータの解決
figma-component-splitter.js 1ファイルに出力されたReactコードをコンポーネント単位に分割
vue-css-extractor.js PlaywrightでComputed Styleを取得しJSONに保存
figma-cache-tools.sh キャッシュの一覧表示、プレビュー、クリーンアップ

これらのユーティリティは、ワークフローの開発の際にClaude Codeで作成していきました。
AIエージェントへの指示は、具体的なロジックではなく「このスクリプトを実行して」という形になります。

## Step 2: Reactコード生成と分割

1. svg-import-converter.js を実行してSVGを変換
   node .claude/tools/svg-import-converter.js {cacheId}

2. figma-component-splitter.js を実行して適切なサイズ感でコンポーネントを分割
   node .claude/tools/figma-component-splitter.js {generatedFile}

このアプローチにより、以下の効果が出ました。

  • AI の判断ポイントが減り、実行が安定する

  • 複雑な処理をコンテキストに含めなくて済む


Playwrightの活用:3つの検証ポイント

本ワークフローでは、各段階で Playwright を役割分担して使います。
用途に応じて Playwright MCP(AI 操作) / Playwright ライブラリ(スクリプト) / Playwright Test(E2E) を使い分けることで、出力を安定させます。

ステップ 活用方法 目的
Step 3: Reactレンダリング検証 Playwright MCP AIエージェントによる即時レンダリング確認
Step 6: Computed Style抽出 Playwrightライブラリ スクリプトによるCSSの抽出
Step 10 最終検証 E2Eテスト・ビジュアル比較

1. React生成コードの即時検証(Step 3: Reactレンダリング検証)

Figma MCP が返す React が、実際にブラウザで成立しているかを確認します。AI が Playwright MCP を呼び出し、遷移・エラーチェック・スクリーンショットを自動化します。

# ページ遷移
mcp__playwright__browser_navigate:
  url: "http://localhost:5173/CampaignPage"

# ローディング完了待ち
mcp__playwright__browser_wait_for:
  textGone: "Loading..."

# スクリーンショット取得
mcp__playwright__browser_take_screenshot:
  filename: "CampaignPage_react.png"

# コンソールエラー確認
mcp__playwright__browser_console_messages:
  level: "error"

# 画像ロード状態確認
mcp__playwright__browser_evaluate:
  function: |
    Array.from(document.querySelectorAll('img'))
      .map(img => ({ src: img.src, loaded: img.naturalWidth > 0 }))

また、即時検証のために TanStack Router のファイルベースルーティングを利用しています。ファイルを所定ディレクトリに置くだけで URL が生え、ルーティング設定の編集を省略できます。

.figma-mcp/src/routes/
├── __root.tsx
├── index.tsx
├── CampaignPage.tsx      # /CampaignPage
└── AnotherDesign.tsx

Step 2: Reactコード生成と分割 では、AIはファイルを配置するだけでよく、ルーティング設定を意識する必要がありません。
これにより、Step 3: Reactレンダリング検証では単に該当URLへアクセスするだけで、即時に描画確認が可能になります。

2. Computed Styleの抽出

Tailwind クラス解釈を AI に任せると、微妙なズレが出ることがありました。そこで、ブラウザが計算した最終 CSS(Computed Style)を取得し、「正解データ」に寄せます。

大量要素の抽出は Playwright MCP よりもライブラリのほうが安定するため、ユーティリティとして実装します。

// 一部抜粋
const { chromium } = require('playwright')

async function extractStyles(componentName) {
  const browser = await chromium.launch()
  const page = await browser.newPage()

  await page.goto(`http://localhost:5173/${componentName}`)

  const styles = await page.evaluate(() =>
    Array.from(document.querySelectorAll('[data-node-id]')).map(el => ({
      nodeId: el.dataset.nodeId,
      className: el.className,
      css: Object.fromEntries(
        Array.from(getComputedStyle(el))
          .map(prop => [prop, getComputedStyle(el).getPropertyValue(prop)])
      )
    }))
  )

  await browser.close()
  return styles
}

ただし Computed Style そのままだとデフォルト値などを含んでしまうので、Tailwind API と照合して実装に必要な情報だけを JSON に残します。
Vue 実装側はこの JSON を参照しながら「確定値」をベースにスタイルを組み立てるため、解釈ブレを減らし再現度を安定させられます。

{
  "componentPath": "XXXX/YYYY",
  "elements": [
    {
      "dataNodeId": "I123:456;789:012",
      "className": "grid-cols-[max-content] inline-grid relative shrink-0 z-[3]",
      "matched": true,
      "css": {
        "grid-template-columns": "296px",
        "display": "inline-grid",
        "position": "relative",
        "flex-shrink": "0",
        "z-index": "3"
      }
    }
  ]
}

3. 最終検証とビジュアル比較

最後に生成された Vue 実装を Playwright Test で検証します。エラー有無に加えて、スクリーンショットで視覚的に追えるようにします。

// 自動生成
import { test, expect } from '@playwright/test'

test('Vue page loads without errors', async ({ page }) => {
  const errors: string[] = []
  page.on('pageerror', e => errors.push(e.message))

  await page.goto('/campaign_page')
  await page.waitForLoadState('networkidle')
  await page.screenshot({ path: 'report/vue.png', fullPage: true })

  expect(errors).toHaveLength(0)
})

React(デザインコンテキスト)とVueの描画結果を比較するテストも自動生成し、視覚的な差分を確認できるようにしています。

// 自動生成
test('Vue vs React comparison', async ({ page }) => {
  await page.goto('http://localhost:3000/campaign_page')
  await expect(page).toHaveScreenshot('campaign_page-baseline.png', {
    fullPage: true,
    maxDiffPixelRatio: 0.1
  })
})

これにより、PlaywrightのHTMLレポート上で、デザインコンテキストからVueへの実装結果を視覚的に確認できます。

一部フォント調整などは今後の課題ですが、レイアウト・余白・重なり順といった観点では、ほぼ期待通りの再現ができているようでした。


(左がデザインコンテキストベースのもので、右がVue実装になります。)

Figmaのデザインコンテキストによる問題の対応

ここからは、Figmaのデザインコンテキストを実際にAIが扱う際に起きていた問題とその解決をいくつかご紹介します。

1. 仮想SVGへの対応

デザインコンテキストを確認すると、SVGが仮想SVGという一つのファイルとして扱われています。

import {
  img2,
  img3,
  img4,
  img5,
  img6,
  img7,
  img10,
  img11,
  img12,
  img14,
  img15,
  img16,
  img17,
  img18,
  img19,
  img20,
  img24,
} from './svg-zzzzzz'

また、次のようなマッピング情報が返されますが、AI がこの対応関係をうまく解釈できず、SVG 配置が崩れることが多くありました。

"svgMapping": {
    "svg-zzzzzz": [
      "/path/to/project/123-456/xxxxxxxxxxxxxxxxxxx.svg",
      "/path/to/project/123-456/yyyyyyyyyyyyyyyyyyy.svg",
      ...
  },

そのため、マッピングを解決して単一 SVG を参照できる形に変換する処理を専用ユーティリティとして用意しています。

import { img2 } from '/path/to/project/123-456/xxxxxxxxxxxxxxxxxxx.svg'
import { img3 } from '/path/to/project/123-456/yyyyyyyyyyyyyyyyyyy.svg'
・・・

2. 予約語の自動リネーム

Figma のデフォルトのレイヤー名は、プログラミング言語や HTML の予約語と衝突しやすいです。たとえば Text / Image / Main をそのままコンポーネント名にすると、標準コンポーネントなどと競合してレンダリングエラーの原因になります。

そこでプロセス内で危険な名前を検出し、安全な名前へリネームするマッピングを生成します。

{
  "renameMap": {
    "Text": "UiText",
    "Main": "UiMain"
  }
}
予約語 リネーム先 理由
Text UiText HTML <text> と競合
Main UiMain HTML <main> と競合
Image UiImage HTML <image> と競合
Button UiButton HTML <button> と競合

(この部分は AI による検出でも可能ですが、デザイナーと相談して命名規約を整えると、より安定する可能性があります。)

ワークフローをClaude Skillsとして定義する

ここまで紹介したワークフローは、最終的に「Claude Skills」として定義し、再利用可能な形にまとめました。

Claude Skillsとは

Claude Skillsは、Claude Codeで繰り返し使うワークフローやプロンプトをパッケージ化する仕組みです。プロジェクトの .claude/skills/ ディレクトリに配置することで、チーム全体で同じワークフローを共有できます。

スキルの構成

今回のFigma→Vue実装ワークフローは、以下のような構成でスキル化しています。

.claude/skills/figma-to-vue/
├── SKILL.md              # スキルの説明とトリガー条件
├── reference/
│   ├── agent-rules.md    # エージェント共通ルール
│   └── tasks/
│       ├── fetch.md      # Step 1: データ取得とキャッシュ保存
│       ├── build.md      # Step 2: Reactコード生成と分割
│       ├── verify.md     # Step 3: Reactレンダリング検証
│       └── ...           # 各ステップの詳細指示
└── .figma-mcp/
    ├── cache/            # Figmaデータのキャッシュ
    └── src/generated/    # 生成されたReactコード

呼び出し方

スキルはClaude Codeで自動的に認識され、トリガー条件に合致すると自動的に適用されます。

ユーザー: このFigmaを実装して
         https://www.figma.com/design/xxx/yyy?node-id=123-456

Claude Code: [figma-to-vue スキルを自動適用]
             Step 1: データ取得とキャッシュ保存

まとめ

今回は「REALITY 思い出アルバム」というリッチなデザインを題材に、Figma MCP のデザインコンテキストを Claude Code で扱い、Vue 実装まで落とし込むワークフローを検証しました。

結果として、タスク分割や Playwright を組み合わせることで、コンテキストを制御し、再現度を検証しながら出力を安定させられる手応えが得られました。
今後はデザインの異なる画面でも同じ手法が通用するか検証範囲を広げつつ、別種のワークフロー設計にも応用していければと思います。


本記事で紹介したパターンが、同様の課題に取り組む方の参考になれば幸いです。


※ 本記事で紹介した構成図やフローは概念的なものであり、実際の実装詳細とは一部異なる場合があります。

REALITY Tech

Discussion