UI を「変数」と「部品」に分解する、はじめての design-system 生成
気になる UI を 1 枚渡すだけで、その UI を構成している色や余白、ボタンやカードといった「部品」、さらにヘッダーやテーブルといった「ブロック」のカタログページを AI に作ってもらえる。これができると、なんとなく綺麗だと思っていた UI が、実は 変数 → 部品 → ブロック の組み合わせで出来ているという当たり前のことが、目に見える形で腹に落ちる。4 つのステップで進めるので、最後まで通して 15 分から 20 分くらい。
このガイドで何が手に入るか
ステップ 1 から 4 まで順番に AI にプロンプトを送ると、最終的に手元に 4 つのコードファイルが出来上がる。それを自分の Next.js プロジェクトに置くと、ブラウザで「色一覧」「余白一覧」「角丸一覧」「使われている部品の一覧」「組み立てられたブロックの一覧」が見える 1 枚のページになる。これがいわゆる design-system ページの簡易版です。
作り終わったあとに改めて元の UI と見比べると、「この色がここで使われているのか」「このボタンとこのバッジは同じ角丸を共有しているのか」「ヘッダーは検索 + アバター + ナビという 3 つの部品の組み合わせで出来ているのか」みたいなことが、なんとなくの感覚じゃなく具体的な 値や構造 として見えるようになる。これが「変数で考える」スタイリングの最初の体感です。
tokens.json と components.json をそのまま使えます。
1変数(トークン)を抜き出す
最初のステップは、入力した UI からトークンと呼ばれる「再利用される値」を全部抜き出してもらう作業です。トークンというのは、色とか余白の何ピクセル分とか、角の丸さの数値とか、フォントサイズとか、要するに UI 全体で使い回されている数値や色のことです。
ここで大事なのは、AI に「ただ色を全部リストアップして」と頼むのではなく、「何のために使われているか(role)」もセットで答えてもらうことです。 #0066ff という色がプライマリーボタンに使われているのか、リンク文字色に使われているのか、ホバー時にだけ出てくる強調色なのか。 role が分かっていると、後で別の UI に応用するときに迷わない。
渡すもの
- 気になる UI のスクリーンショット画像(コードがあるなら一緒に貼ってもいい)
- 余裕があれば「何のアプリか」「何の画面か」「想定ユーザー」も 1 行ずつ
使うプロンプト
あなたの役割: UI スタイリングのトークン (色 / 余白 / 角丸 / 線 / 影 / フォント) を抽出する design system expert。
# 入力
## 良い UI (= 完成形)
[ここに UI のスクリーンショット or コード or Figma URL を貼る]
## (任意) コンテキスト
- 何のアプリ:
- 何の画面:
- 想定ユーザー:
# タスク
入力 UI を観察し、 使われている全トークンを下のスキーマで JSON 出力する。
## ルール
- 同じ値は重複させず 1 つに統合する (例: #ffffff と white は同じ)
- 出現頻度の多い順に並べる
- 各 token に role (用途の 1 行) を必ず書く
- 段階数は UI に従う (色なら 5-7 / 余白なら 7-10 段階あたり)
- 無いカテゴリ (例: shadow を使ってない) は空オブジェクト {} で出す
## 出力スキーマ
{
"color": {
"bg": { "<name>": { "value": "#rrggbb", "role": "<役割>" } },
"fg": { "<name>": { "value": "#rrggbb", "role": "<役割>" } },
"text": { "<name>": { "value": "#rrggbb", "role": "<役割>" } }
},
"spacing": { "<key>": { "value_px": "<number>", "role": "<役割>" } },
"radius": { "<key>": { "value_px": "<number>", "role": "<役割>" } },
"ring": { "<key>": { "width_px": "<number>", "color": "<css>", "role": "<役割>" } },
"shadow": { "<key>": { "css": "<css>", "role": "<役割>" } },
"typography": { "<key>": { "size_px": "<number>", "weight": "<number>", "role": "<役割>" } }
}
# 出力フォーマット
```json ブロックの JSON のみ。 説明文や前置きは書かない。
戻ってくるもの
AI から JSON という形式で返ってきます。 JSON は「キーと値のセットで書く、メモ帳でも読めるただのテキスト」というだけのもので、 AI に決まった形で結果を返してもらいたい時の中間データとしてよく使われます(変換や Figma 登録の話は Figma 登録ガイド で扱います)。 中身は色なら 5 個から 7 個、余白なら 7 個から 10 個くらいに整理されているはずです。次のステップでこの JSON をそのまま使うので、コピーして手元のファイル(仮に tokens.json とでも名付けて)に保存しておきます。
この JSON が最終的にステップ 3 で Web ページの形になると、こんなふうに色が一覧で見える状態になります。
#??? みたいに伏字で返ってきたら、プロンプトの末尾に「画像から目視で色を読み取り、 hex で出力して」と付け足して再度送る。これでだいたい直ります。
2部品(コンポーネント)を抜き出す
次に、同じ UI から「再利用される部品」を抜き出します。ボタン、バッジ、カード、入力欄、テーブル、ヘッダー、こういったもの。ステップ 1 で抜き出した変数(色や余白)が、ここで「ボタンというパッケージ」にまとまっていく感じです。
ここでも注意点があって、細かすぎず大きすぎず、 5 個から 15 個くらいに収めてもらうのがちょうどいい。文字 1 つを部品と呼ぶには細かすぎるし、ページ全体を部品にしてしまうと再利用できない。 AI がこの粒度を外しそうな時に効くのが、プロンプト内の「reusable な部品」というキーワードです。
渡すもの
- ステップ 1 で使ったのと同じ UI 画像(または同じコード)
- ステップ 1 で出てきた
tokens.jsonの中身を全部コピーして貼り付け
使うプロンプト
あなたの役割: UI の reusable な部品 (component) を識別してカタログ化する design system curator。
# 入力
## 良い UI
[ステップ 1 で使ったのと同じ UI 画像 or コード]
## tokens.json (ステップ 1 の出力)
[ステップ 1 の結果をそのまま貼る]
# タスク
UI から reusable な component を 5〜15 個 識別し、 下のスキーマで JSON 出力する。
## ルール
- 細かすぎ (例: 1 文字レベル) も大きすぎ (= ページ丸ごと) も避け、 再利用される部品を選ぶ
- 同じ 形 の複数 state (default/hover/pressed など) や種類 (primary/secondary) は variants にまとめる
- 各 component が使うトークンを tokens_used で参照 (ステップ 1 の path 形式 = color.fg.primary spacing.3.5 等)
- example_jsx は実物を Tailwind class で書く (動く JSX)
## 出力スキーマ
{
"components": [
{
"name": "<部品名>",
"description": "<1 行説明>",
"variants": ["<variant1>", "<variant2>"],
"tokens_used": ["color.fg.primary", "radius.full", "spacing.3.5"],
"example_jsx": "<button className='...'>label</button>"
}
]
}
# 出力フォーマット
```json ブロックの JSON のみ。 説明文や前置きは書かない。
戻ってくるもの
components という配列の中に、 5 個から 15 個分の部品定義が並んだ JSON が返ってきます。各部品には「どのトークンを使っているか」が明示されていて、これがあとで「ボタンと入力欄が同じ角丸を共有している」みたいな関係を見える化してくれます。これも components.json として手元に保存します。
ステップ 3 まで通すと、各部品はこんなふうにカード形式で並びます。名前・variants・参照しているトークンまでセットで見える化されます。
3部品を組み立てて「ブロック」を抜き出す
ここまでで「変数(色や余白)」と「部品(ボタンやバッジ)」が揃いました。でも実際の UI を見ていると、ボタン単体やバッジ単体ではなく、それらが組み合わさってもう一段大きい 「ブロック」 になっていることに気づきます。ロゴ + ナビ + 検索 + 通知 + アバターが横一列で並んだ「ヘッダー」、見出し + フィルター + テーブル本体がセットになった「データテーブル」、サイドバー、ヒーロー、こういう 中サイズの構造単位 を抜き出すのがこのステップです。
ブロックを意識して分解できると、自分が UI を作る時に「この画面のヘッダー部分は、あの参考 UI のヘッダーブロックの組み立て方を借りられるな」みたいな移植がしやすくなります。部品単位ではなく 構造単位 で参考にできるようになる、というのが狙いです。
渡すもの
- ステップ 1・2 で使ったのと同じ UI 画像(または同じコード)
- ステップ 1 の
tokens.jsonと ステップ 2 のcomponents.jsonの中身
使うプロンプト
あなたの役割: UI の reusable な block (= component を組み合わせた構造単位) を識別する design system architect。
# 入力
## 良い UI
[ステップ 1・2 で使ったのと同じ UI 画像 or コード]
## tokens.json (ステップ 1 の出力)
[ステップ 1 の結果をそのまま貼る]
## components.json (ステップ 2 の出力)
[ステップ 2 の結果をそのまま貼る]
# タスク
UI から reusable な block を 3〜7 個 識別し、 下のスキーマで JSON 出力する。
## block とは
- 複数の component (場合によっては tokens) を組み合わせて作る、 ページ内で意味的にまとまった構造単位
- 例: header / global navigation / page hero / data table / filter bar / sidebar / page footer
- component より大きく、 page より小さい中サイズのまとまり
## ルール
- 細かすぎ (= 単一 component) も大きすぎ (= ページ全体) も避ける
- 各 block が含む components を components_used で参照 (ステップ 2 の name)
- 必要に応じて直接使う tokens を tokens_used で参照 (背景色やレイアウト spacing 等)
- purpose に 1 行で「このブロックの役目 (何のために存在するか)」 を書く
- example_jsx は実物を Tailwind class で書く (動く JSX)
## 出力スキーマ
{
"blocks": [
{
"name": "<block名>",
"purpose": "<このブロックの役目 (1 行)>",
"components_used": ["<component name>", "..."],
"tokens_used": ["color.bg.surface", "spacing.6"],
"example_jsx": "<header className='...'>...</header>"
}
]
}
# 出力フォーマット
```json ブロックの JSON のみ。 説明文や前置きは書かない。
戻ってくるもの
blocks という配列の中に、 3 個から 7 個分のブロック定義が並んだ JSON が返ってきます。各ブロックには「どの component を組み合わせて作られているか」が明示されていて、これがあとで「自分のプロジェクトのヘッダーは、このブロック構成を真似てみよう」という時の 設計図 になります。これも blocks.json として手元に保存します。
次のステップ 4 のページ生成で、この blocks.json もまとめて渡すと、 design-system ページの末尾に「Blocks セクション」が出来て、組み立てたブロックがプレビュー + 構成要素 (components_used / tokens_used) と一緒に並んで見えるようになります。
4分解ページを作る
ここまでで JSON が 3 つ揃いました。最後のステップは、この 3 つを材料にして、ブラウザで開ける Web ページの形(React + Tailwind のコード)を作ってもらう作業です。出来上がるのは 4 つのファイルで、それぞれ役割が違います。
1 つ目の tokens.ts は、トークンを TypeScript の型付きの定数として書き出したファイル。 2 つ目の components.tsx は、各部品を実際の React コンポーネントとして実装したファイル。 3 つ目の blocks.tsx は、各ブロックを React コンポーネントとして実装し、 使っている部品と変数の参照も持つファイル。 4 つ目の design-system/page.tsx は、 これら 3 つを読み込んでカタログ表示する Web ページ本体です。 Next.js プロジェクトに 4 ファイルを所定の場所に置けば、ブラウザで開けるようになります。
渡すもの
- ステップ 1 の出力(
tokens.json) - ステップ 2 の出力(
components.json) - ステップ 3 の出力(
blocks.json)
使うプロンプト
あなたの役割: React + Tailwind の design system カタログページを生成する。
# 入力
## tokens.json (ステップ 1 の出力)
[ここに貼る]
## components.json (ステップ 2 の出力)
[ここに貼る]
## blocks.json (ステップ 3 の出力)
[ここに貼る]
# タスク
以下の 4 ファイルを生成する:
1. _design-system/tokens.ts — typed const として tokens を export
2. _design-system/components.tsx — 各 component を React で実装 + メタカタログ export
3. _design-system/blocks.tsx — 各 block を React で実装 + メタカタログ export
4. design-system/page.tsx — tokens / components / blocks をデータ駆動でカタログ表示
## 構造の指示
### tokens.ts
- type 定義 (ColorToken / SpacingToken / RadiusToken / etc.)
- export const tokens = { color: {...}, spacing: {...}, ... } as const satisfies ...
- 入力 JSON の値をそのまま型付けして export する
### components.tsx
- 各 component を function コンポーネントとして実装
- export const components = [{ name, description, variants, tokens, examples: [...] }, ...]
- examples の中で実物の React 要素を返す
### blocks.tsx
- 各 block を function コンポーネントとして実装 (内部で components.tsx の部品を import して組み立てる)
- export const blocks = [{ name, purpose, components_used, tokens_used, examples: [...] }, ...]
- examples の中で実物の React 要素を返す (= ヘッダーやテーブル本体)
### design-system/page.tsx
- tokens を category 別に table 形式でカタログ表示 (色は swatch + hex + role)
- components を card 形式で各 example を render
- blocks を大きめのプレビューカードで render (構成 components_used と tokens_used も一緒に表示)
- セクション順: Color → Spacing → Radius → Ring → Shadow → Typography → Components → Blocks
- Tailwind のみで、 外部 UI library (shadcn / radix UI 等) は使わない
## 出力フォーマット
4 つの ```tsx ブロックを順に。 各ブロックの 1 行目に // === <filepath> === コメントでファイルパスを明示。
戻ってくるもの
tokens.ts components.tsx blocks.tsx design-system/page.tsx の 4 つのファイルが、それぞれコードブロックとして返ってきます。これを自分の Next.js プロジェクトの該当する場所に保存して、開発サーバーを起動。ブラウザで /design-system を開くと、入力した UI の 変数・部品・ブロック のカタログが見えるようになっています。
Color の下には Spacing / Radius / Ring(線) が続き、その下に Shadow / Typography、続いて Components、最後に Blocks(組み立てた構造単位) という構造で並びます。全体だとこんな見え方です。
うまく動かなかった時のチェックリスト
ここで詰まる人が多そうなポイントを 3 つだけ書いておきます。
1 つ目、色や角丸が画面に反映されない。 これは Tailwind 側の設定が足りていないことが多い。 Tailwind v4 を使っているなら @theme inline を、 v3 なら tailwind.config.js で CSS variable 経由で読み込む設定が必要です。 AI に「私のプロジェクトの Tailwind 設定はこうです」と渡して、必要な追記を教えてもらうのが速い。
2 つ目、フォントが崩れる。 Inter フォントの読み込みが入ってない可能性が高いです。 next/font/google 経由で読み込むか、 globals.css に CDN 経由の link を書き足すと直ります。
3 つ目、コードが途中で切れた。 長い tsx ファイルだと AI の出力が打ち切られることがあります。「続きから書いて」と頼むか、ステップ 4 のプロンプトを 1 ファイルずつに分けて 4 回送る、で解決します。 blocks.tsx は中身が大きくなりがちなので、ここで分割するのが特に有効です。
出来上がったあとに、もう一度 UI を眺める
最後にひとつだけお願いがあって、 4 ファイルを配置してブラウザで開いた後、ぜひ元の UI 画像をもう一度横に並べて眺めてみてほしい。「あの綺麗に見えたボタンの角は、ちゃんと radius.lg という名前の値だった」「あのいい感じの余白は spacing.4 という名前で管理されている」「あの目を引くヘッダーは、ロゴ + ナビピル + アバターという 3 つの部品の組み合わせで出来ている」みたいなことが、見比べた瞬間に腹に落ちる。
これが「変数で考える」という言葉の意味の最初の手触りです。同じ手順を、別の UI、別のサービスでも繰り返すと、自分の中に「分解する目」が育っていきます。