
Claude DesignとClaudeにデザインシステムを作らせた話
デザイナーをアサインできない立ち上げでも、画面の一貫性は最初に決めておきたい。ただ、Figmaを整える時間はない。
そういう案件で、デザインシステムの設計そのものをAIに任せてみた記録です。デザインは Claude Design、トークン定義から実装・コミットまでは Claude で進めました。
「AIがきれいな画面を作ってくれた」で終わる話ではありません。
- 出てきたのは、色名ではなく役割ベースのデザイントークン体系だった
- そのトークンを104ファイル・約5,800行に一括適用できた
- 結果、個別画面の作り込みが「トークンと用意済みのコンポーネントを組み合わせるだけ」になった
ただし、アクセシビリティとダークモードは、自動では埋まりませんでした。
なので本記事は「AI万能礼賛」ではなく、AIが強い領域と、人間の設計意志が必要な領域の境界線を引くための記録です。

進め方:ワイヤー → モック → デザイン → トークン定義の4段階
Figmaでデザインを作り込んでから実装に入る、という進め方をとる時間はありませんでした。
そこでとった方針が、デザインの前に、画面の骨組みをテキストで固めるというものでした。
実際の流れは、この4段階です。
- ワイヤー:Markdownで画面構成と遷移を書く
- モック:データの形をinterfaceで先に決める
- デザイン:Claude Design で見た目を当てる
- トークン定義:Claude でコードに落とし、実装まで進める
Figmaを立てず、Markdownでワイヤーを書いた
Figmaは立てませんでした。代わりに書いたのがMarkdownです。
doc/user-flow.md:288行の権限別の画面遷移doc/screens/:18画面分のワイヤー、約1,900行
このMarkdownが、そのまま次の工程の入力になりました。
理由はシンプルで、AIにとってはFigmaの矩形よりも、構造化されたテキストのほうが読める入力だからです。「この画面には何があって、どのロールがどこへ遷移するのか」がテキストで書いてあれば、それはもう仕様書です。
骨組みを先に立てる:モックを単一ソースにまとめる
デザインに入る前に、もうひとつやったことがあります。
APIが完成する前にフロントエンドを走らせるため、shared/mocks/index.ts にハードコードを集約するという下ごしらえです。
ポイントは、ただモックを置くのではなく、先にinterfaceを切っておくことです。
こうしておくと、あとから本物のAPIに差し替えるのがラクになります。データの形をあらかじめ決めてあるので、呼び出す側のコードを書き換えずに済むからです。
土台がないところにデザインを当てると、画面ごとに場当たり的な判断が増えて、結局バラバラになります。逆に骨組みが固まっていれば、デザインは「上から一気に塗る」作業にできます。この順序が、後の一括適用につながりました。
本題:AIにデザインシステムを設計させてみた
指示は「パステルグリーン基調」くらいの抽象度から
ワイヤーができたところで、Claude Design にデザインを当ててもらいました。投げた指示は、ほぼこれだけです。
システムのUIです。パステルグリーンを基調に、落ち着いたトーンでデザインしてください。
我ながら雑です。色コードも指定していないし、コンポーネント一覧も渡していません。
正直、この抽象度では使えないものが返ってくると思っていました。ところが見た目自体、そのまま画面に載せられる水準で返ってきました。ただ、それ以上に驚いたのは、コードに落としたときの中身のほうでした。
出てきたのは、色名ではなく役割ベースのデザイントークンだった
Claude Design で固まった見た目を、Claude に tailwind.config.ts として書き起こしてもらったところ、こういう構造になっていました。
// tailwind.config.ts(抜粋)
theme: {
extend: {
colors: {
// サーフェス階層
canvas: "#F7FAF8",
surface: "#FFFFFF",
"surface-alt": "#F1F6F3",
hairline: "#E2EAE5",
// テキスト階層(4段階)
"text-base": "#1F2A24",
"text-sub": "#4A5A52",
"text-muted": "#7C8C84",
"text-disabled": "#AEBAB4",
// ブランド(セージグリーン)
brand: { /* 50 - 900 */ },
// サイドバー専用(ディープフォレスト系)
sidebar: { /* ... */ },
},
},
}
ここで「おっ」となったのが、命名が生の色名になっていないことです。
green-100 gray-500 ではなく、canvas / surface / surface-alt / hairline。テキストも text-base → text-sub → text-muted → text-disabled という4段階の階層になっています。
つまり、「何色か」ではなく「どういう役割か」でトークンが切られているわけです。
ちなみに、こちらは一度も「役割で名前を付けて」とは指示していません。この命名は、指示していないところから出てきたものです。
なぜ役割ベースだと強いのか
理由は単純で、あとから色を変えても意味が壊れないからです。
たとえば bg-green-50 と書いた箇所が画面中に散らばっていると、「この緑は背景の緑なのか、ブランドの緑なのか、成功状態の緑なのか」が判別できません。ブランドカラーを変えたい、というだけの変更が、全ファイル目視確認になります。
一方 bg-surface-alt と書いてあれば、意味は「一段沈んだ面」で固定です。色の値だけ差し替えれば、意図は保たれたまま見た目が変わります。命名そのものが仕様になっている、ということです。
ステータスの色を、画面ごとに決めさせない
何かを作って、確認して、公開する。そういう流れのあるアプリには、たいてい「状態」があります。下書き、レビュー中、確定待ち、公開済み、ロック済み。画面上では、こうした状態をバッジやラベルの色で見分けさせることになります。
そして、ここがいちばん崩れやすいところです。
原因はシンプルで、状態バッジの色を、画面を作るたびにその場で決めてしまうからです。一覧画面では黄色っぽく塗り、詳細画面では薄いグレーで作り、別の画面ではまた違う色になる。チームなら人によって判断が割れますし、ひとりで作っていても、先週の自分と今日の自分で選ぶ色は変わります。誰も手を抜いていないのに、同じ「レビュー中」が画面ごとに違う見た目になるわけです。
これに対して出てきたのが、状態そのものをトークンとして定義するというやり方でした。しかも1状態につき、文字色・背景色・枠線色の3点セットで、です。
status: {
draft: { fg: "...", bg: "...", border: "..." },
review: { fg: "...", bg: "...", border: "..." },
confirm: { fg: "...", bg: "...", border: "..." },
published: { fg: "...", bg: "...", border: "..." },
locked: { fg: "...", bg: "...", border: "..." },
}
そのうえで、このトークンを読む側のコンポーネント(StatusPill や Chip)を shared/ui に用意しました。画面側は状態名を渡すだけで済みます。
<StatusPill status="review" />画面を作る人に「色を選ぶ余地」がないので、そもそもズレようがありません。
一貫性を「気をつけて守るもの」から「仕組みで守られるもの」に変えた、ということです。人間のレビューで守ろうとすると必ずどこかで漏れますが、トークンとコンポーネントを必ず経由する構造なら、漏れる隙間がそもそもありません。
適用フェーズ:全画面に一括適用 → 重要画面だけ手仕上げ
トークンができたら、次は適用です。
まず、104ファイル・約5,800行に一括適用
1回のコミットで、104ファイル・約5,800行を全画面に一括適用しました。
「全画面のボタンをこのトークンに置き換えて」が、一度の指示で済む規模です。
そのあと、コア画面だけ個別に作り込み
次にやったのが、いちばん使用頻度の高い画面の仕上げです。具体的には、生の <button> や <header> を、Button / Avatar / Chip / StatusPill といった用意済みのコンポーネントに置き換えていきました。
ここで実感したのが、デザインシステムがあると、個別画面の作り込みが「トークンと用意済みのコンポーネントを組み合わせるだけ」になるということでした。
手仕上げにはデザインセンスが要る、と思われるかもしれません。ですが実際にやることは、用意されたものから選ぶ作業でした。
色を選ぶ、余白を決める、影の強さを悩む。こういう判断がトークン設計の時点で終わっているので、画面側でやることは「どのトークンを、どこに置くか」だけ。デザイン判断ではなく、レイアウト判断に落ちます。
結果、「一括で土台を作り、重要画面だけ手で仕上げる」という二段構えが成立しました。
何がうまくいき、何は自動で埋まらなかったか
うまくいったもの:抽象的な指示から、一貫した見た目が出た
「パステルグリーン基調で」というあの雑な一言から、18画面をまかなえるトークン体系が出てきたのは、素直に想定以上でした。
しかも、立ち上げの範囲では新しいトークンを足す必要がほとんど発生しなかったのが大きかったです。役割で切ってあるので、新しい画面も既存トークンの組み合わせで足りる。画面を追加するたびに「この色どうする?」で立ち止まらずに済んだ、ということです。
自動では埋まらなかったもの①:アクセシビリティ
一方で、アクセシビリティは初版がかなり薄かったです。
ここで言うアクセシビリティとは、たとえばこういうことです。
- 画面読み上げソフトを使ったときに、そのボタンや入力欄が何なのか正しく伝わるか
- マウスを使わず、キーボードだけで操作を完了できるか
- モーダルを閉じたとき、操作位置(フォーカス)が元いた場所に戻るか
いずれも見た目には一切現れません。だからこそ、初版では抜け落ちていました。
あとから追加したのは、次のような対応です。
- 選択式の入力欄 / textarea に、「これは選択肢のグループです」「この欄は何を入力する欄です」と読み上げソフトに伝える目印を付ける(
radiogroup/aria-label) - モーダルやドロワーに、「これはダイアログです」という目印を付け、閉じたときにフォーカスを元に戻す
見た目は一発で揃うのに、こうした対応は出てきません。ここが境界線だと思っています。
見た目の一貫性は一撃で出せても、アクセシビリティには別の設計意志が要ります。
「フォーカスをどこに戻すべきか」「このUIは読み上げソフトにどう読まれるべきか」は、見た目からは導き出せません。指示しないと出てこない領域です。
自動では埋まらなかったもの②:ダークモード非対応
こちらは、もっとはっきりしています。
darkModeの設定なしprefers-color-schemeの実装なし- トークンは単一値のライト専用
ダークモードには非対応です。ただ、ここは前向きな見方もできます。役割ベースでトークンが切られているので、拡張の下地はできているからです。
surface の値を1つ増やす形で対応できる設計になっているので、「全ファイルを目視で直す」という最悪の展開にはなりません。ここでも役割ベース命名が効いています。
まとめ:抽象的な指示から、役割ベースのトークンへ
Claude Design と Claude にデザインシステムを設計させた記録でした。
- ワイヤーをMarkdownで書いておくと、そのままAIへの入力になります
Figmaを立てるより、構造化されたテキストのほうがAIには読めます。 - 役割ベーストークンが、一貫性の源泉になります
green-100ではなくsurface-alt。意味で切ってあるので、色を差し替えても意図が壊れません。 - 状態の色をトークンで先に決めておくと、画面ごとのズレが起きません
画面を作る人に「色を選ぶ余地」を残さないのが、一貫性の近道です。 - AIは「一貫した見た目」は一撃で作れますが、アクセシビリティとモード拡張には別途の設計意志が要ります
ここが、AIと人間の役割の境界線です。
デザイナーをアサインできない立ち上げでも、一貫性の土台は先に作れます。抽象的な指示をトークン体系に落とすところまでAIに任せる、というやり方が現実的な選択肢になりました。
もし今、同じような立ち上げで悩んでいる方がいたら、まずは「色を決める」のではなく「役割を決める」ところから始めてみてください。そこさえ通れば、あとは一括適用で一気に前に進めます。


