AIUI スタイリング / ガイド 01

UI を「変数」と「部品」に分解する、はじめての design-system 生成

気になる UI を 1 枚渡すだけで、その UI を構成している色や余白、ボタンやカードといった「部品」、さらにヘッダーやテーブルといった「ブロック」のカタログページを AI に作ってもらえる。これができると、なんとなく綺麗だと思っていた UI が、実は 変数 → 部品 → ブロック の組み合わせで出来ているという当たり前のことが、目に見える形で腹に落ちる。4 つのステップで進めるので、最後まで通して 15 分から 20 分くらい。

対象
Figma は触れるけど、デザインシステムや Tailwind はまだそこまで触っていない人
準備するもの
気になる UI の画像 1 枚(スクショで OK)と、 AI ツール(Claude / Cursor / Claude Code / v0 のどれか)
所要時間
10〜15 分
出来上がるもの
渡した UI の色・余白・部品・ブロックが一覧で見える Web ページ(コード 4 ファイル)

このガイドで何が手に入るか

ステップ 1 から 4 まで順番に AI にプロンプトを送ると、最終的に手元に 4 つのコードファイルが出来上がる。それを自分の Next.js プロジェクトに置くと、ブラウザで「色一覧」「余白一覧」「角丸一覧」「使われている部品の一覧」「組み立てられたブロックの一覧」が見える 1 枚のページになる。これがいわゆる design-system ページの簡易版です。

作り終わったあとに改めて元の UI と見比べると、「この色がここで使われているのか」「このボタンとこのバッジは同じ角丸を共有しているのか」「ヘッダーは検索 + アバター + ナビという 3 つの部品の組み合わせで出来ているのか」みたいなことが、なんとなくの感覚じゃなく具体的な 値や構造 として見えるようになる。これが「変数で考える」スタイリングの最初の体感です。

補足 もう一つ別の使い方として、「ダメな UI から良い UI に変わっていく 4 段階の playground」を作るプロンプトテンプレもあります(比較プレイグラウンド版を開く)。最初の 2 ステップは同じ内容なので、ここで作った tokens.jsoncomponents.json をそのまま使えます。

1変数(トークン)を抜き出す

最初のステップは、入力した UI からトークンと呼ばれる「再利用される値」を全部抜き出してもらう作業です。トークンというのは、色とか余白の何ピクセル分とか、角の丸さの数値とか、フォントサイズとか、要するに UI 全体で使い回されている数値や色のことです。

ここで大事なのは、AI に「ただ色を全部リストアップして」と頼むのではなく、「何のために使われているか(role)」もセットで答えてもらうことです。 #0066ff という色がプライマリーボタンに使われているのか、リンク文字色に使われているのか、ホバー時にだけ出てくる強調色なのか。 role が分かっていると、後で別の UI に応用するときに迷わない。

渡すもの

使うプロンプト

あなたの役割: 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 ページの形になると、こんなふうに色が一覧で見える状態になります。

design-system ページの Color セクション。background / foreground / text の3カテゴリーで色がカード形式で一覧表示されている
ステップ 3 まで通したあとの色一覧(preview)。 background / foreground / text の 3 カテゴリーで、各色に name / role / hex が並ぶ。
うまくいかなかった時のチェック 画像だけ渡したのに色のコードが #??? みたいに伏字で返ってきたら、プロンプトの末尾に「画像から目視で色を読み取り、 hex で出力して」と付け足して再度送る。これでだいたい直ります。

2部品(コンポーネント)を抜き出す

次に、同じ UI から「再利用される部品」を抜き出します。ボタン、バッジ、カード、入力欄、テーブル、ヘッダー、こういったもの。ステップ 1 で抜き出した変数(色や余白)が、ここで「ボタンというパッケージ」にまとまっていく感じです。

ここでも注意点があって、細かすぎず大きすぎず、 5 個から 15 個くらいに収めてもらうのがちょうどいい。文字 1 つを部品と呼ぶには細かすぎるし、ページ全体を部品にしてしまうと再利用できない。 AI がこの粒度を外しそうな時に効くのが、プロンプト内の「reusable な部品」というキーワードです。

渡すもの

使うプロンプト

あなたの役割: 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・参照しているトークンまでセットで見える化されます。

design-system ページの Components セクション。Button / Status / Avatar の各部品が、Examples(実物)・Variants・参照Tokens のセットでカード表示されている
ステップ 3 まで通したあとの部品カタログ(preview)。 Button / Status / Avatar の各カードに、実物 + variants + 使っているトークン名が並ぶ。

3部品を組み立てて「ブロック」を抜き出す

ここまでで「変数(色や余白)」と「部品(ボタンやバッジ)」が揃いました。でも実際の UI を見ていると、ボタン単体やバッジ単体ではなく、それらが組み合わさってもう一段大きい 「ブロック」 になっていることに気づきます。ロゴ + ナビ + 検索 + 通知 + アバターが横一列で並んだ「ヘッダー」、見出し + フィルター + テーブル本体がセットになった「データテーブル」、サイドバー、ヒーロー、こういう 中サイズの構造単位 を抜き出すのがこのステップです。

ブロックを意識して分解できると、自分が UI を作る時に「この画面のヘッダー部分は、あの参考 UI のヘッダーブロックの組み立て方を借りられるな」みたいな移植がしやすくなります。部品単位ではなく 構造単位 で参考にできるようになる、というのが狙いです。

渡すもの

使うプロンプト

あなたの役割: 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) と一緒に並んで見えるようになります。

うまく動かなかった時のチェック ブロックが細かすぎ (= component とほぼ同じ) になったら、プロンプトに「ボタン単体や入力欄単体は block ではなく component なので除外して」と付け足す。逆に大きすぎ (= 1 ブロックで画面の半分以上を占める) なら、「block は画面の 1 セクション程度の大きさ」と制約を加える。

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 ファイルを所定の場所に置けば、ブラウザで開けるようになります。

渡すもの

使うプロンプト

あなたの役割: 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(組み立てた構造単位) という構造で並びます。全体だとこんな見え方です。

design-system ページの Spacing / Radius / Ring セクション。各値の使われ方をミニチュアで示したカードが並ぶ
Spacing(余白) / Radius(角丸) / Ring(線)の段階。 各カードに小さい図解 + px 値 + Tailwind class 名が並ぶ。
design-system ページの Shadow / Typography セクション。影の段階と、見出し・本文・ラベル・microの文字サイズ階層がカード形式で並ぶ
Shadow(影) と Typography(文字サイズ階層)。 px・weight・letter-spacing まで見える形で整理される。

うまく動かなかった時のチェックリスト

ここで詰まる人が多そうなポイントを 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、別のサービスでも繰り返すと、自分の中に「分解する目」が育っていきます。