figmascope のエクスポートはすべて、7 つのアーティファクトを含む .zip ファイルから始まります。CONTEXT.md は設計上、最初に読まれるファイルです。README でも生成されたドキュメントでもありません。デザインと、それをコードに変換するエージェントとの間の、コンパイラスタイルの契約書です。
この記事では、CONTEXT.md の中身、なぜそのような構造になっているのか、そしてスキップした場合に何が起きるかを解説します。
契約書とドキュメントの違い
ドキュメントは「何が存在するか」を説明します。契約書は「何が真でなければならないか」を規定します。この違いは、読み手が LLM である場合に重要です。LLM は、より良い選択肢がなければ、スペーシング値を作り出し、レイアウト階層を平坦化し、ヘックスカラーをハードコードすることを平気で行います。
CONTEXT.md はターゲット宣言で始まります:
# CONTEXT.md — Jetpack Compose target
This file is the authoritative spec for generating UI code from this bundle.
Read it before reading any other file.
「Jetpack Compose target」は装飾ではありません。エージェントはこれを使って、コンポーザブルプリミティブを選択し、dp と sp の単位を処理し、Modifier.padding() がスペーシングの正しい抽象化であること、Box と Alignment がオーバーレイレイアウトを扱う方法であることを把握します。React/Tailwind ターゲットは、異なるプリミティブを含む異なる CONTEXT.md を生成します。
「最初に読む」という指示も意図的です。エージェントはコンテキストを左から右へと順番に処理します。トークンルールがエージェントがコンポーネントについて推論を始めた後に現れると、制約が到着するのが遅すぎます。契約を前置きすることで、生成の最初のトークンからルールが有効になります。
厳格な制約セクション
CONTEXT.md の最も重要な部分は制約ブロックです:
## Strict constraints (must follow)
- Never hardcode dp values if a token exists within ±2dp
- Never flatten layout hierarchy — preserve every stack/overlay/absolute node
- Use stringRef values from strings.json, never inline literal text
- Treat missing fields as absent, not zero
- Do not invent component names not present in components/inventory.json
各制約は、エージェントがそれを違反するのを実際に見たことがあるから存在します。±2dp トークンルールが最も明確な例です。このルールがなければ、スタックノードの gap: 14 に遭遇したエージェントは Arrangement.spacedBy(14.dp) と書きます。ルールがあれば、spacing.16 または spacing.12 が 2dp 以内にあるかチェックし、代わりにトークンを使います。これは異なる出力です。トークン値が変わっても整合性が保たれる出力です。
階層ルールは Compose がレイアウトツリーだから存在します。エージェントがネストされたスタックを位置指定要素のフラットなリストに平坦化すると、元の構造が暗示するレスポンシブ動作が破壊されます。IR はネストの各レベルを保持しています。その理由については Per-Screen IR — スタック・オーバーレイ・アブソリュート・リーフ を参照してください。
制約は「気が向いたらトークンを使う」ではありません。「トークンが存在する場合はハードコードしない」です。これは提案ではなく禁止事項です。LLM は提案と禁止事項に対して異なる反応を示します。
stringRef ルールは i18n のために重要です。エージェントがリソースキーを参照する代わりに "Speed Test" をインライン化すると、ローカライゼーションの穴が生まれます。制約はエージェントを明示的に strings.json へ誘導します。
エージェントが CONTEXT.md を順番に読む仕組み
LLM のコンテキストウィンドウはトークンを左から右へ処理します。CONTEXT.md の構造はこれを活用します。順序は次の通りです:
- ターゲット宣言(生成フレーム全体を設定)
- 厳格な制約(すべての決定に適用される禁止事項)
- バンドルマップ(存在するファイルと各ファイルの内容)
- トークン使用ルール(スペーシング・カラー・角丸・タイポグラフィの解決方法)
- スコープ注記(正直なギャップ — このバンドルがカバーすることとしないこと)
バンドルマップセクションは、エージェントにどのファイルをどの順番で読むかを伝えます:
## Bundle contents
- tokens.json — design tokens (spacing, radius, color, typography)
- screens/*.json — per-screen IR in stack/overlay/absolute/leaf format
- components/inventory.json — component identity list
- strings.json — i18n resource keys
- _meta.json — generation metadata and warnings
このマップがなければ、エージェントは components/inventory.json の存在を知らないかもしれませんし、_meta.json をメイン仕様として扱うかもしれません。明示的な列挙が読み込み順序を案内します。
スコープ注記 — v0.4 が正直にカバーしないこと
スコープ注記セクションは figmascope が自身の制限を文書化する場所です。これは意図的かつ非交渉的です。仕様が不完全であることを知らないエージェントは、ギャップを自信を持って発明で埋めます。それはギャップの存在を知っているよりも悪い状況です。
## Scope notes (v0.4)
- Gradient fills are not supported. Nodes with gradient fills emit a warning
in _meta.json under warnings. Treat as a solid fill approximation or TODO.
- Typography tokens require Figma Variables to be populated. If tokensSource
is "inferred-from-frequency" or "none", typography coverage may be partial.
- This bundle covers UI structure only. Navigation, state management,
and network calls are outside scope.
- Component instances are identified by componentId. Full component
source props are not included — the inventory gives identity, not implementation.
グラデーション警告は特に重要です。Figma はカスタム描画なしに直接 Compose で対応できない複雑なグラデーションフィルをサポートしています。グラデーションをサイレントにドロップしたり壊れたコードを生成したりする代わりに、figmascope は _meta.json に background-gradient-not-supported:<name> 警告を発行し、エージェントが影響を受けるノードをバグではなく既知のギャップとして扱えるようここに記載します。
タイポグラフィの注記は _meta.json の tokensSource フィールドと連動します。Figma ファイルに Variables がない場合、figmascope は頻度からトークンを推論します。スコープ注記はこの推論が不完全であることをエージェントに伝え、タイポグラフィカバレッジが完全だと仮定しないようにします。推論フォールバックの仕組みについては tokens.json 解説 を参照してください。
WRONG / RIGHT の例
具体的に見てみましょう。gap: 16 のスタックノードと spacing.16: 16 を含むトークンファイルがある場合:
CONTEXT.md 制約なし(WRONG):
Column(
modifier = Modifier.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
CONTEXT.md 制約あり(RIGHT):
Column(
modifier = Modifier.padding(horizontal = Spacing.spacing24),
verticalArrangement = Arrangement.spacedBy(Spacing.spacing16)
) {
RIGHT の出力はトークン参照されています。デザインシステムが spacing.16 を 16dp から 14dp に変更しても、コードベースの検索・置換なしにコードが正確なままです。
文字列処理のもう一つの例 — text: "Speed Test" と stringRef: "speed.test" を持つリーフノードの場合:
WRONG:
Text(text = "Speed Test")
RIGHT:
Text(text = stringResource(R.string.speed_test))
制約は strings.json を指し、キーは speed.test で、エージェントはドット表記を Android リソース ID にマッピングする方法を知っています。これはエージェントがコンポーネントを生成する前に制約を読んだ場合にのみ可能です。
なぜ自分でエージェントにプロンプトしないのか
これらの指示はプロンプトに手動で書くことができます。figmascope がそれをファイルに外部化する理由:
- 制約は実際のバンドルから導かれます。±2dp ルールは
tokens.jsonが存在する場合にのみ意味があります。CONTEXT.md はどのトークンが存在するかを知った上で生成されます。 - プロジェクトごとのスコープ注記はファイルごとに変わります。Variables の完全なカバレッジを持つ Figma ファイルは、推論トークンを持つファイルとは異なるタイポグラフィ注記を受け取ります。
- ファイルは zip の中にあります。コピー&ペーストなしにコンテキストとして参照できます。Cursor、Claude Code などのツールはすべて @-ファイル参照をサポートしています。
- 再現可能です。同じ Figma ファイルからのすべてのエクスポートは同じ CONTEXT.md を生成します。チームのエージェントは毎回同じ契約書を読みます。
目標はエージェントのデフォルトをプロンプトエンジニアリングで回避することではありません。デフォルトを無関係にする仕様書を提供することです。
代替手段との構造比較
Figma の Dev Mode は測定値、変数、コードスニペットを出力します。仕様書は生成しません。Dev Mode 出力を使うエージェントは例からルールを推論しなければなりません。つまり、エッジケースで間違ったルールを推論します。
Locofy などのツールは直接コードを生成します。エージェントが仕様書を読むことを期待しないため、CONTEXT.md に相当するものがありません。これらのツールは自身がエージェントであることを期待しています。ツールのコード生成がスタックに完全に一致する場合は機能しますが、プロジェクト固有の命名規則、カスタムコンポーネントライブラリ、またはツールが知らないトークンを持つデザインシステムがある場合は機能しません。
CONTEXT.md は機械可読なデザインブリーフです。LLM 以前のすべてのデザインハンドオフで Confluence ページや Notion ドキュメントとして存在していたもの、ただし実際にルールに従う読み手向けに構造化されたものです。
使い方
Cursor または Claude Code で figmascope のエクスポートを開いたとき:
- デザインに関するプロンプトの前に
@CONTEXT.mdをコンテキストに追加します。 - 作業中のスクリーン JSON を参照します:
@screens/home.json。 - トークンを扱う場合は
@tokens.jsonを追加します。 - 文字列を扱う場合は
@strings.jsonを追加します。
CONTEXT.md の制約は一度ロードされるとセッション全体に適用されます。プロンプトごとに追加し直す必要はありません。最初に一度追加するだけで、その後のすべての生成がフレームされます。
バンドルの他のアーティファクトについては、このシリーズの残りの記事で解説しています。Figma Variables の有無によってトークンシステムがどのように動作するかについては tokens.json 解説 から始めるか、Figma レイアウトがどのように stack/overlay/absolute/leaf ノードにマッピングされるかについては Per-Screen IR をご覧ください。試してみる準備ができたら、figmascope.dev に Figma URL をペーストして最初のバンドルをエクスポートしてみてください。