Figma → HTML/CSS/JSランディングページ
このスキルはFigmaのランディングページデザインを、静的なHTML/CSS/JSに変換します。 作業はブロックごとに進められ、各ブロックの後にエージェントが停止し、ユーザーの確認を待ちます。
コーディングルール(構造、BEM、単位、画像、ホバー効果など)は、このスキルの templates/AGENTS.md に定義されています。ステップ3でこのファイルがプロジェクトルートにコピーされ、以降は常に適用されます。
要件
- Figma MCP が接続されていること(
get_metadata、get_design_context、get_screenshot、可能であればget_variable_defs)。接続されていない場合は、ユーザーにその旨を伝えて停止します。 - ユーザーが
https://www.figma.com/design/:fileKey/:fileName?node-id=1-2形式のリンクを提供していること。 - ローカルサーバー、スクリーンショット、WebP変換にはNode.jsが必要です。初回実行時の
npxコマンドはパッケージをネットワークからダウンロードするため、Codexが許可を求める場合がありますが、これは正常な動作です。
作業手順
以下のステップを順に、飛ばさずに実行します。
ステップ 1. リンクを解析する
fileKey:/design/の後の部分。リンクに/branch/:branchKey/が含まれる場合はbranchKeyを使用します。nodeId:node-idの値。ハイフンをコロンに置き換えます(1-2→1:2)。- リンクは、ドキュメント全体、ページ、デスクトップフレーム、またはモバイルフレームを指す場合があります。タイプはステップ2で特定します。
ステップ 2. マップの作成
- リンクのノードに対して
get_metadataを呼び出します(ドキュメント全体の場合はnode-idなし、またはルートページを指定)。 - ランディングページのバージョンフレーム(デスクトップ、モバイル、時にはタブレット)を見つけます。幅はデザインから特定し、標準値を当てはめないでください。また、モーダルウィンドウ(ポップアップ)やモバイルメニューのフレームがページ内にあれば、それらも個別に見つけます。
- 各フレーム内で、トップレベルのセクション(ヘッダー、ヒーロー、メリット、お客様の声、フッターなど)を抽出します。
- バージョン間でセクションを対応付けます:「デスクトップセクション ↔ モバイルセクション」(レイヤー名、順序、内容に基づく)。
- ユーザーにマップを表示し、確認を待ちます:
幅: デスクトップ 1440, モバイル 375
1. header — 12:30 ↔ 40:2
2. hero — 12:45 ↔ 40:18
3. advantages — 13:10 ↔ 41:5
...
モーダルウィンドウ/メニュー:
- mobile-menu — 45:12
- popup-order — 46:01
モバイルバージョンがない場合は、その旨を伝え、どうするかユーザーに質問します。
ステップ 3. プロジェクトの骨組みとベースUI
- プロジェクトルートに
AGENTS.mdがない場合、スキルフォルダからtemplates/AGENTS.mdをコピーします。その中の「このランディングページ」セクション(幅、コンテナ、フォント)をステップ2のデータに基づいて記入します。ブレークポイントはデフォルトのままにします。このレイアウトのコーディングが他の幅で崩れる場合にのみ変更し、その新しい値を記録します。 - 構造を作成します:
index.html、style.css、script.js、assets/icons/ index.html:<!doctype html>、lang="ja"、charset、viewport、ランディングページの内容に応じた<title>と<meta name="description">。ファビコンは追加しません。script.jsはdeferで読み込みます。- フォント:
get_design_context/get_variable_defsから取得し、必要なウェイトのみをGoogle Fontsからdisplay=swapで読み込みます。フォントがGoogle Fontsにない場合は、ユーザーにファイルを依頼し、@font-faceでassets/fonts/から読み込みます。 style.css: リセット(AGENTS.md参照)、次に:rootに色、フォント、余白、コンテナ幅の変数を定義します。値はget_variable_defsから取得します(デザインに変数がある場合)。それ以外の場合は、デザインから収集して丸めます。- ベースコンポーネント: マクロで共通する要素(ボタン、セクションタイトル、入力フィールド)を再利用可能なクラス(
.btn、.section-title、.input、.container)として抽出します。デザインに(hover、focus、activeの)状態を持つUIキットがない場合、1回だけ、プロジェクト全体の標準的なボタンとリンクのホバールールをユーザーに質問します。回答をAGENTS.mdの「このランディングページ」セクションの「UI状態」行に記録します。 - ツール(1回限り):
- バックグラウンドでプロジェクトルートからローカルサーバーを起動します:
npx -y serve -l 3000(代替:python3 -m http.server 3000)。すべてのスクリーンショットはhttp://localhost:3000から取得し、file://からは取得しません。 - スクリーンショットの撮影方法を確認します: Playwright MCP が接続されていればそれを使用し、そうでなければ
scripts/shot.cjsスクリプト(「自己チェック」参照)を使用します。 - 画像コンバーターを確認し(「画像」参照)、結果を記憶します。
- バックグラウンドでプロジェクトルートからローカルサーバーを起動します:
- 骨組みができたことをユーザーに伝え、最初のブロックに進みます。
ステップ 4. ブロックごとの実装
マップの各セクションについて、上から下へ厳密に順番に(フォームを含むセクションも含む):
- データ. デスクトップセクションとそのモバイルペアに対して
get_design_contextとget_screenshotを呼び出します。- 応答が切り詰められていたり大きすぎる場合は、セクションに対して
get_metadataを呼び出し、子ノードを個別に取得します。 - 応答内のコード(通常はReact + Tailwind)は、サイズ、色、構造の参考としてのみ使用します。スタイルをコピーしないでください。
- 応答が切り詰められていたり大きすぎる場合は、セクションに対して
- 画像. ブロックのアセットをダウンロードして処理します(「画像」参照)。
- コーディング. HTMLブロックを
index.htmlに、スタイルをstyle.cssに(コメント/* ===== hero ===== */で区切った個別セクションとして)、JS(必要な場合)をscript.jsに追加します。ステップ3.6のベースクラスを使用し、ボタンやタイトルのコードを重複させないでください。最初にデスクトップ、次にclamp()とメディアクエリを使用してモバイルまでレスポンシブにします。- モバイル幅の
headerには、バーガーメニューをすぐに実装します(マップのフレームに基づくか、ユーザーに確認)。 - ブロックにフォームがある場合、基本的なブラウザバリデーション属性(
required、type="email"、type="tel"、pattern)を使用して実装しますが、JS送信ロジックは追加しません。
- モバイル幅の
- 自己チェック. 現在のセクションのみを、デスクトップ、モバイル、および中間幅(1024、768)でスクリーンショットを撮影します。Figmaのスクリーンショットと比較します: 余白、サイズ、フォント、色、改行。問題を見つけたら修正し、再度スクリーンショットを撮影します。
- Playwright MCP がある場合: 各幅でセクション要素のスクリーンショットを撮影します。
- MCPがない場合: スキルフォルダのスクリプトを使用します:
スクリプトは# 1回限り mkdir -p .screens && cp <スキルフォルダ>/scripts/shot.cjs .screens/ npm i playwright --prefix .screens && npx -y playwright install chromium # 各ブロック後 node .screens/shot.cjs http://localhost:3000 .hero 1440,1024,768,375.screens/<セクション>-<幅>.pngを保存し、ページが画面幅を超えて表示される場合は⚠ 水平スクロールを出力します。この警告が出た場合は、原因を修正します(bodyにoverflow: hiddenを設定して隠さない)。 - セクションのスクリーンショットはブロック確認後に削除します。スクリプト自体と
.screens/内のnode_modulesはステップ6で削除します。
- 報告と停止. ユーザーに簡潔に報告します: 何をしたか、デザインからどのような逸脱があったかとその理由、ブロックのユニークなインタラクティブ要素(カードの非標準ホバー、スライダーパラメータ)について確認が必要な点。確認を待ちます。 ユーザーの修正を同じブロックに適用し、再度確認を待ちます。
- 「OK」の後でのみ、次のブロックに進みます。
ステップ 5. モーダルウィンドウとフォームロジック
すべてのメインセクションのコーディング後:
- ステップ2で見つかった隠しモーダルウィンドウ(ポップアップ)を実装し、開閉トリガー(ボタン、
Esc、オーバーレイクリック)を接続します。動作はAGENTS.mdの「JavaScript」セクションに従います。 - 各ウィンドウを個別のブロックとして扱います: 開いた状態でのスクリーンショットによる自己チェック、報告、確認待ち。
- ページ上のすべてのフォームが正しく動作することを確認します。データ送信(
fetch/AJAX)は、ユーザーが自ら要求し、エンドポイントまたは仕様を提供するまで実装しません。
ステップ 6. 最終処理
最終ステップの確認後、全幅でページ全体をチェックし、一時ファイルとフォルダ(assets/_raw/、.screens/ 全体)を削除し、バックグラウンドのローカルサーバーを停止し、未使用のスタイルと画像を削除します。簡潔なサマリーを記述します。
画像
- ブロックのアセットはFigma MCPの応答から取得します(
get_design_context内の画像リンク)。localhostアドレスが返された場合は、そのままダウンロードします。プレースホルダーやサードパーティのアイコンセットは使用しません。 - 一時フォルダ
assets/_raw/<ブロック>/にダウンロードします。ラスター画像は、デザイン上のサイズの2倍(2x)の大きさでダウンロードします。 - レスポンシブ画像: モバイルバージョンでトリミングされた(異なるアスペクト比)または意味的に異なる画像が使用されている場合、両方のバージョン(
hero-bg-desktop.webpとhero-bg-mobile.webp)をダウンロードし、<picture>で読み込みます。 - 分類:
- インタラクティブ要素(ボタン、リンク)内の小さなアイコンで、ホバー時に色が変わるもの(
fill/stroke) → HTMLにインライン<svg fill="currentColor">として(またはCSSmask-imageで)。 - 静的なアイコン、ロゴ、単純なグラフィック →
assets/icons/にSVGとして、<img>で読み込み(大きなグラフィックの場合はブロックフォルダ)。アイコンがラスターで提供された場合は、ユーザーに伝えます。 - 写真や複雑な画像 →
assets/<ブロック>/にWebPとして。
- インタラクティブ要素(ボタン、リンク)内の小さなアイコンで、ホバー時に色が変わるもの(
- WebP変換、品質80。試行順序:
npx -y sharp-cli -i in.png -o out.webp -f webp -q 80(Node.jsが必要)cwebp -q 80 in.png -o out.webp- どちらもない場合 → 元の形式を維持し、1回だけ、作業を止めずにユーザーに(Node.jsまたは
cwebpを)インストールするよう伝えます。
- WebPが元のファイルよりも大きい場合 → 元のファイルを維持します。
- ファイル名: ラテン文字、小文字、ハイフン区切り、意味のある名前(
hero-photo.webp、arrow-right.svg)。 - ブロック確認後、
assets/_raw/<ブロック>/を削除します。
ホバー、アニメーション、スライダー
- ホバー: 基本ボタンとリンクについては、
AGENTS.mdの「UI状態」行のルールに従います。ブロック内にユニークなインタラクティブカード/要素があり、デザインに状態がない場合、勝手に作成せず、ブロックの報告でユーザーに質問します。 - スクロールアニメーションとカスタムアニメーション: ユーザーが自ら要求した場合のみ。まず、何を起こしたいのかを明確にします。シンプルなもの(スクロール時の出現)は
IntersectionObserver。複雑なもの(ホイール連動、スティッキーセクション、パララックス)はCDN経由のGSAP、ユーザーの同意後。常にprefers-reduced-motionを考慮します。 - スライダー: シンプルなもの(矢印、ドット、スワイプ)は、CSS
scroll-snapと少量のJSで自作。複雑なもの(無限ループ、自動再生、エフェクト、連動スライダー)はCDN経由のSwiper。
よくある問題
- Figmaの応答が切り詰められる → セクションに対して
get_metadata、その後子ノードに対してget_design_context。 - モバイルセクションのペアやモバイルメニューフレームが見つからない → ユーザーに質問し、勝手に作成しない。
- フォントの幅がデザインと合わない → ウェイトと
letter-spacingを確認し、フォントサイズを調整しない。 - 画像がダウンロードできない → Figma MCPが実行されているか確認。リンクは変更せずに使用。
- スクリーンショットでフォントやアイコンが崩れる → サーバーが
localhost:3000で実行されており、file://ではなくそのサーバーからスクリーンショットを撮影していることを確認。 - スクリプトが「水平スクロール」と出力する → 画面幅より広い要素(固定幅、大きな
gap、端の装飾)を見つけて修正。装飾要素が端にある場合は、そのセクションにoverflow-x: clipを設定。 - アクセシビリティや技術的制限によるデザインからの逸脱 → 許容されますが、ブロックの報告でその旨を伝える。