「コンポーネントは増え続けるのに、それぞれがどう振る舞うのかチーム全体で把握しきれない」——フロントエンドの規模が大きくなるほど、この課題は深刻になります。Storybook は本来この問題への回答ですが、Story を書く工数と Visual Regression の運用負担が原因で、導入したものの形骸化しているチームを何度も見てきました。
私自身、個人開発で 4 サイトを並行運用している中で、UI のリグレッションを目視レビューで防ぐ方法には限界を感じてきました。Claude Code を Storybook の運用に組み込んでから、Story の網羅率と Visual Regression の精度が同時に改善し、結果としてリリースサイクルが大幅に短縮されています。ここではその過程で確立した本番運用パターンを、実装コードとともに体系的に解説します。
なぜ今、Claude Code と Storybook を組み合わせるべきなのか
Storybook が抱える本質的な課題は「コンポーネントは書けるが Story を書く時間がない」という点です。これはテストコードと同じ構造の問題で、価値を理解していても優先度が下がりやすい。Claude Code は、この「書きたいけれど書けない」領域を埋めるのに非常に向いています。
具体的に Claude Code が得意とするのは次の領域です。コンポーネントの Props を読み取り、想定される使用パターンを推定して複数のバリアントを Story 化する作業、args と argTypes の型定義を TypeScript の型から自動生成する作業、デザインシステム上の制約(カラートークン・スペーシング・タイポグラフィ)に沿った Story 構成の整備、これらは Claude Code がほぼ自走で進められます。
一方で、Visual Regression のスナップショット差分判定や、デザイナーとの合意形成といった「人間が判断すべき領域」には踏み込まないように設計するのが重要です。私のプロジェクトでは、Story の生成と CI 統合は Claude Code に任せ、差分が出た場合の承認は必ず人間が行う、という分担にしています。
想定する読者と前提環境
本記事は次のような環境を想定しています。Next.js 15 もしくは Vite 7 + React 19 を使った中規模以上のフロントエンド、Storybook 9.x、TypeScript 5.6 以上、CI には GitHub Actions、Visual Regression には Chromatic もしくは Playwright Visual Comparisons、エディタは VS Code + Claude Code 拡張、もしくはターミナルでの Claude Code CLI 直接利用です。これより小さい規模でも応用は可能ですが、本記事の運用パターンは「Story が 50 個を超えてきたあたりから明確に効いてくる」設計になっています。
Storybook 環境の自動セットアップと CLAUDE.md 設計
Claude Code に Story を生成させる前に、リポジトリの CLAUDE.md にプロジェクト固有の規約を記述しておくと、生成される Story の品質が一段階上がります。私が実際に使っている CLAUDE.md のコンポーネント周辺セクションを抜粋します。
## Storybook ルール
- Story は CSF 3.0 形式で記述する(CSF 2.0 は禁止)
- ファイル配置: `src/components/{Category}/{Component}/{Component}.stories.tsx`
- 必須エクスポート: `Default` , `Loading` , `Error` , `Empty` (該当する状態がある場合)
- `argTypes` は Props の型から自動生成し、 `description` を必ず付ける
- `parameters.layout` は最上位コンポーネントは `fullscreen` 、それ以外は `centered`
- Tailwind トークン以外の色・スペーシングを Story 内で直書きしない
- Visual Regression のため、アニメーションは `parameters.chromatic.disableAnimations: true` を付ける
## デザイントークン
- カラー: `--color-primary` , `--color-surface` , `--color-text` を `globals.css` で定義
- スペーシング: 4px グリッド(Tailwind の `gap-1` 〜 `gap-12` を使用)
- タイポ: `font-display` (見出し) / `font-body` (本文)
このルールを CLAUDE.md に書いておくと、Claude Code は Story を生成するたびに参照し、規約を逸脱しません。逆に、これがないとプロジェクト全体で args の書き方や parameters の設定がバラバラになり、後から統一する作業が発生します。
初期セットアップを Claude Code に依頼する
新規プロジェクトに Storybook を導入する場合、次のようなプロンプトで一気にセットアップが完了します。
claude "このプロジェクトに Storybook 9 をインストールしてください。条件:
- Vite + React + TypeScript の構成
- @storybook/addon-essentials, @storybook/addon-a11y, @storybook/addon-interactions を含む
- preview.tsx で Tailwind の globals.css をインポート
- main.ts で stories パスを 'src/components/**/*.stories.@(tsx|mdx)' に設定
- package.json に storybook と build-storybook スクリプトを追加
- 既存の tsconfig.json を壊さないこと
完了したら storybook を起動して動作確認のスクリーンショットを撮らずに、ファイル一覧だけ報告してください。"
ポイントは「動作確認のスクリーンショットは不要」と明示することです。Claude Code は確認のために dev server を立ち上げてしまうことがあり、CI 環境やリモート開発では時間を浪費します。最終確認は人間が行う前提で、Claude Code には「ファイル変更まで」を任せるのが安定運用のコツです。
既存コンポーネントから Story を一括生成する実装パターン
Storybook 導入の最大のハードルは「既存コンポーネント数十〜数百個分の Story を書ききること」です。ここを Claude Code に任せると劇的に楽になりますが、ナイーブに「全部 Story を書いて」と頼むと品質がバラつきます。私が確立した手順は次のとおりです。
1. コンポーネントカタログを生成する
最初に、Story を書くべきコンポーネントの一覧と優先順位を Claude Code に整理させます。
claude "src/components/ 以下のすべてのコンポーネントについて、次の情報を JSON で出力してください:
- name: コンポーネント名
- path: ファイルパス
- props: Props の型定義から抽出した必須/任意/型情報
- variants_estimated: Props の組み合わせから推定されるバリアント数
- complexity: 'low' | 'medium' | 'high'(依存コンポーネント数や状態管理の有無で判断)
- has_story: すでに .stories.tsx が存在するか
出力先: tmp/components-catalog.json"
このカタログがあると、Story 生成を「complexity が low のものから順に」「同じディレクトリのものをまとめて」といった粒度で発注できます。
2. プロンプトテンプレートを使い回す
Story 生成は同じプロンプトを使い回せるよう、.claude/commands/story-gen.md にテンプレート化しておきます。
# Story 生成テンプレート
引数で渡されたコンポーネントについて、CLAUDE.md の Storybook ルールに従って .stories.tsx を生成してください。
## 必須要件
1. CSF 3.0 形式
2. Default Story を含む
3. Props の型から argTypes を自動生成
4. 状態を持つコンポーネントの場合は Loading / Error / Empty Story を追加
5. 子要素を取るコンポーネントは Slots Story を追加(Story 内でモック子要素を配置)
6. アクセシビリティに配慮し、aria-label を持つ要素は a11y addon が認識できる形にする
7. Tailwind カスタムクラスを使う場合は preview.tsx に import 済みであることを前提とする
## 出力
- ファイルパス: コンポーネントと同じディレクトリ
- 実行後、生成した Story 名のリストを箇条書きで報告
このコマンドを claude /story-gen src/components/Button/Button.tsx のように呼び出すだけで、規約に沿った Story が生成されます。
3. 生成 Story の品質チェック
Claude Code が生成した Story は、人間がレビューする前に自動チェックを通します。私は次のスクリプトを CI と pre-commit に組み込んでいます。
// scripts/validate-stories.ts
import { glob } from 'glob' ;
import { readFile } from 'fs/promises' ;
interface ValidationError {
file : string ;
rule : string ;
message : string ;
}
async function validateStories () : Promise < ValidationError []> {
const errors : ValidationError [] = [];
const files = await glob ( 'src/components/**/*.stories.tsx' );
for ( const file of files) {
const content = await readFile (file, 'utf-8' );
// CSF 3.0 形式チェック
if ( ! content. includes ( 'export default' )) {
errors. push ({ file, rule: 'csf-meta' , message: 'meta オブジェクトの default export がありません' });
}
// Default Story の存在チェック
if ( ! /export const Default/ . test (content)) {
errors. push ({ file, rule: 'default-story' , message: 'Default Story が定義されていません' });
}
// argTypes の存在チェック(Props を持つコンポーネントの場合)
if (content. includes ( 'args:' ) && ! content. includes ( 'argTypes:' )) {
errors. push ({ file, rule: 'arg-types' , message: 'args を使用していますが argTypes が定義されていません' });
}
// 禁止パターンチェック
if ( /style= \{\{ . * background/ . test (content)) {
errors. push ({ file, rule: 'no-inline-color' , message: 'インラインで background を指定しています。Tailwind トークンを使用してください' });
}
}
return errors;
}
validateStories (). then (( errors ) => {
if (errors. length > 0 ) {
console. error ( 'Story 検証エラー:' );
errors. forEach (( e ) => console. error ( ` ${ e . file } [${ e . rule }]: ${ e . message }` ));
process. exit ( 1 );
}
console. log ( '✅ すべての Story が規約に準拠しています' );
});
このバリデータを通すことで、Claude Code が生成した Story の品質が一定以上に保たれ、後から書き直す工数が激減します。
Visual Regression Testing の本番運用設計
Visual Regression は「導入は簡単、運用は難しい」の典型例です。スナップショットの差分が出るたびに 100 枚以上の画像を確認するのは現実的ではありません。私が実運用に耐える設計として確立したのは、次の三層構造です。
第一層: Chromatic で UI レビュー必須化
Chromatic は Storybook のホスティングと Visual Regression を統合したサービスで、PR ごとに変更があった Story だけを自動でハイライトしてくれます。GitHub Actions との統合例を示します。
# .github/workflows/chromatic.yml
name : Chromatic
on :
push :
branches : [ main ]
pull_request :
jobs :
chromatic :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
with :
fetch-depth : 0 # Chromatic は履歴が必要
- uses : actions/setup-node@v4
with :
node-version : 22
cache : npm
- run : npm ci
- run : npm run build-storybook
- uses : chromaui/action@v11
with :
projectToken : ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
onlyChanged : true # 変更があった Story のみテスト
exitZeroOnChanges : true # 差分があってもジョブは成功扱い
autoAcceptChanges : main # main への push は自動承認
env :
CHROMATIC_PROJECT_TOKEN : ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
onlyChanged: true と autoAcceptChanges: main の組み合わせが運用上のキモです。これにより「PR で変更があった Story だけが Chromatic 上でレビュー対象になる」「main にマージされた変更は自動で新ベースラインになる」という流れが作れます。
第二層: Playwright Visual Comparisons でユーザーフロー検証
Chromatic はコンポーネント単位の Visual Regression に最適ですが、ページ遷移を含むフローのリグレッションは検出できません。ここを Playwright Visual Comparisons で補完します。Storybook の @storybook/test-runner と組み合わせて、Story を実際にブラウザで開きスクリーンショットを取得します。
// .storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner' ;
import { toMatchImageSnapshot } from 'jest-image-snapshot' ;
const config : TestRunnerConfig = {
setup () {
expect. extend ({ toMatchImageSnapshot });
},
async postVisit ( page , context ) {
// インタラクション後のスクリーンショットを比較
if (context.tags?. includes ( 'visual' )) {
const image = await page. screenshot ({ fullPage: true });
// @ts-expect-error jest-image-snapshot は型定義が拡張される
expect (image). toMatchImageSnapshot ({
customSnapshotIdentifier: context.id,
failureThreshold: 0.02 ,
failureThresholdType: 'percent' ,
});
}
},
};
export default config;
failureThreshold: 0.02 は経験的に「フォントレンダリングの差分は無視するが、レイアウト崩れは検出する」しきい値です。0 にすると CI 環境のフォント差で常に失敗し、運用が回りません。
第三層: Lighthouse CI で視覚以外の劣化検出
見た目は同じでもパフォーマンスが劣化していたら本番では問題です。これを Lighthouse CI で補足します。Storybook ビルドした静的サイトに対して定期的に走らせ、Performance スコアの劣化を PR コメントで通知する構成が便利です。Claude Code には .lighthouserc.json の生成と GitHub Actions のワークフロー追加を任せられます。
Interaction Testing と Accessibility 検証の自動化
Storybook 9 から Interaction Testing が標準サポートされ、ユーザー操作のテストも Story 内で完結するようになりました。Claude Code に依頼する際のプロンプトを示します。
claude "src/components/Form/LoginForm/LoginForm.stories.tsx に Interaction Story を追加してください。
要件:
1. play 関数で次のシナリオを実装:
- メールアドレス未入力で送信 → エラーメッセージ表示確認
- 不正なメール形式で送信 → エラーメッセージ表示確認
- 正しい入力で送信 → onSubmit が呼ばれることを確認
2. @storybook/test の userEvent と expect を使用
3. 各シナリオは別 Story として独立させる
4. await step() でシナリオを段階的に記述
参考: 既存の SignupForm.stories.tsx と同じスタイルで書いてください。"
実装される Story の例は次のようになります。
// src/components/Form/LoginForm/LoginForm.stories.tsx
import type { Meta, StoryObj } from '@storybook/react' ;
import { expect, userEvent, within } from '@storybook/test' ;
import { LoginForm } from './LoginForm' ;
const meta : Meta < typeof LoginForm> = {
title: 'Form/LoginForm' ,
component: LoginForm,
parameters: { layout: 'centered' },
tags: [ 'autodocs' ],
};
export default meta;
type Story = StoryObj < typeof LoginForm>;
export const Default : Story = {
args: { onSubmit : () => {} },
};
export const ValidationEmpty : Story = {
args: { onSubmit : () => {} },
play : async ({ canvasElement , step }) => {
const canvas = within (canvasElement);
await step ( '空のまま送信' , async () => {
await userEvent. click (canvas. getByRole ( 'button' , { name: 'Sign in' }));
await expect (canvas. getByText ( 'メールアドレスを入力してください' )). toBeInTheDocument ();
});
},
};
export const ValidationInvalidEmail : Story = {
args: { onSubmit : () => {} },
play : async ({ canvasElement , step }) => {
const canvas = within (canvasElement);
await step ( '不正なメール形式で送信' , async () => {
await userEvent. type (canvas. getByLabelText ( 'Email' ), 'not-an-email' );
await userEvent. click (canvas. getByRole ( 'button' , { name: 'Sign in' }));
await expect (canvas. getByText ( '正しい形式のメールアドレスを入力してください' )). toBeInTheDocument ();
});
},
};
このように Story として書いておくと、Storybook UI でも実行できますし、CI では test-storybook コマンドで一括実行できます。
Accessibility は addon-a11y を CI ブロッカーに
@storybook/addon-a11y を組み込み、test-runner で a11y 違反があれば CI を失敗させる設定にしておきます。
// .storybook/test-runner.ts に追記
import { getStoryContext } from '@storybook/test-runner' ;
import { injectAxe, checkA11y } from 'axe-playwright' ;
const config : TestRunnerConfig = {
// 既存の設定に追加
async preVisit ( page ) {
await injectAxe (page);
},
async postVisit ( page , context ) {
const storyContext = await getStoryContext (page, context);
if (storyContext.parameters?.a11y?.disable) return ;
await checkA11y (page, '#storybook-root' , {
detailedReport: true ,
detailedReportOptions: { html: true },
axeOptions: { runOnly: { type: 'tag' , values: [ 'wcag2a' , 'wcag2aa' ] } },
});
},
};
WCAG 2.1 Level AA を機械的にチェックするだけでも、コントラスト不足や aria-label 漏れの大半を検出できます。完全な対応は人間のレビューが必要ですが、機械チェックで弾けるものを CI で止めるだけで、後工程の負担は大きく減ります。
CI/CD 統合とコンポーネント変更の自動レビュー
Storybook の Story と Visual Regression を CI に統合した上で、さらに踏み込んで「PR で変更があったコンポーネントを Claude Code に自動レビューさせる」仕組みを組んでいます。これは GitHub Actions の pull_request イベントで Claude Code を起動し、変更ファイルと Story の差分をコメントとして返す構成です。
# .github/workflows/component-review.yml
name : Component Review
on :
pull_request :
paths :
- 'src/components/**'
jobs :
review :
runs-on : ubuntu-latest
permissions :
contents : read
pull-requests : write
steps :
- uses : actions/checkout@v4
with :
fetch-depth : 0
- name : Get changed files
id : changes
run : |
CHANGED=$(git diff --name-only origin/main...HEAD -- 'src/components/**/*.tsx' | tr '\n' ' ')
echo "files=$CHANGED" >> $GITHUB_OUTPUT
- name : Run Claude Code review
if : steps.changes.outputs.files != ''
run : |
npx @anthropic-ai/claude-code review \
--files "${{ steps.changes.outputs.files }}" \
--prompt-file .github/prompts/component-review.md \
--output review.md
env :
ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
- name : Post review comment
if : steps.changes.outputs.files != ''
uses : marocchino/sticky-pull-request-comment@v2
with :
path : review.md
component-review.md には次のような観点を書いておきます。
# コンポーネント変更レビュー
以下のコンポーネントが変更されました。CLAUDE.md の規約と照らし合わせて、次の観点でレビューしてください。
1. **Story の追従** : 変更された Props や状態に対応する Story が更新されているか
2. **アクセシビリティ** : 新たに追加された UI 要素に role / aria-label が適切に設定されているか
3. **デザイントークン** : 直書きの色・スペーシングが含まれていないか
4. **Breaking Change** : 既存の Props 削除や型変更がないか。あれば移行ガイドの提案
5. **パフォーマンス** : 不必要な再レンダリングを誘発する変更がないか
各項目について、問題があれば該当行を引用しコメント。問題なければ `✅` のみ。
出力フォーマット: Markdown チェックリスト形式。
このレビューは「人間レビュアーの代替」ではなく「人間レビュアーが見る前のチェックリスト」として運用します。Claude Code が見落としを指摘したら人間が判断する、というワークフローにすることで、レビュー品質と速度が両立します。
デザインシステムのドキュメント化と Storybook MCP サーバー
Storybook はもともとドキュメント機能(Docs Mode、MDX、autodocs)が強力ですが、Claude Code と組み合わせるとさらにその真価を発揮します。私が運用しているのは、Storybook の MCP サーバーを通じて Claude Code が「現在登録されている全 Story の一覧」「各 Story の Props 仕様」「使用例」にアクセスできる構成です。
これにより、新規ページを実装する際に Claude Code が「既存の Button コンポーネントの variant 一覧」を参照しながら適切なバリアントを選んでくれます。再利用可能なコンポーネントが既にあるのに新規で似たものを作ってしまう、という典型的な事故を防げます。
// .claude/mcp-servers/storybook.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/index.js' ;
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' ;
import { glob } from 'glob' ;
import { readFile } from 'fs/promises' ;
const server = new McpServer ({ name: 'storybook-catalog' , version: '1.0.0' });
server. tool (
'list_components' ,
'プロジェクトの全コンポーネントとその Story 一覧を返す' ,
async () => {
const files = await glob ( 'src/components/**/*.stories.tsx' );
const components = await Promise . all (
files. map ( async ( file ) => {
const content = await readFile (file, 'utf-8' );
const titleMatch = content. match ( /title: \s * ['"] ( [ ^ '"] + ) ['"] / );
const stories = [ ... content. matchAll ( /export const ( \w + ):/ g )]. map (( m ) => m[ 1 ]);
return {
file,
title: titleMatch?.[ 1 ] ?? 'Unknown' ,
stories: stories. filter (( s ) => s !== 'default' ),
};
})
);
return { content: [{ type: 'text' , text: JSON . stringify (components, null , 2 ) }] };
}
);
server. tool (
'get_component_props' ,
'指定したコンポーネントの Props 型定義を返す' ,
{ component: { type: 'string' } },
async ({ component }) => {
const file = `src/components/${ component }/${ component }.tsx` ;
const content = await readFile (file, 'utf-8' );
const propsMatch = content. match ( /(?:type | interface) \s + \w * Props \s * = ? \s * \{ ( [ ^ }] + ) \} / s );
return {
content: [{ type: 'text' , text: propsMatch?.[ 1 ]. trim () ?? 'Props 型が見つかりません' }],
};
}
);
const transport = new StdioServerTransport ();
await server. connect (transport);
.claude/settings.json に MCP サーバーを登録しておくと、Claude Code は会話中にこれらのツールを呼び出して Story カタログを参照できます。これは Claude Code MCP サーバー構築完全ガイド で紹介した内製 MCP の応用パターンです。
よくある落とし穴と運用上の判断基準
最後に、私が実運用で踏んだ落とし穴と、その回避策を共有します。
落とし穴 1: Story を作りすぎて Visual Regression が常に失敗する
「あらゆるバリアントを Story にする」と決めると、Story 数が爆発的に増え、Chromatic の月間スナップショット数を超過します。私が採用しているルールは「Visual Regression 対象 Story には tags: ['visual'] を付ける」「それ以外は documentation 用のみ」という分離です。Visual Regression 対象は人間がレビューする価値があるものに絞り、エッジケースの組み合わせは Interaction Testing で担保します。
落とし穴 2: Claude Code が CSF 2.0 と CSF 3.0 を混在させる
CSF(Component Story Format)はバージョンアップで API が変わっています。Claude Code は学習データの中に古いコードも含むため、明示的に「CSF 3.0」を指示しないと CSF 2.0 形式で生成することがあります。CLAUDE.md の Storybook ルールに必ず「CSF 3.0 形式で記述する(CSF 2.0 は禁止)」と明記してください。
落とし穴 3: Tailwind v4 の Story が真っ白になる
Tailwind CSS v4 は CSS-first 設計のため、Storybook の preview に明示的に import './globals.css' しないとスタイルが何も適用されず、Story が真っ白になります。Claude Code に Storybook を初期セットアップさせる際は、preview.tsx で Tailwind の CSS を必ず import するよう指示してください。
落とし穴 4: Visual Regression のしきい値を 0 にして CI が常に赤
CI 環境のフォントレンダリングは macOS のローカルとピクセル単位で異なります。failureThreshold: 0 にすると常に差分が出て運用が崩壊します。私のおすすめは 0.01〜0.02(パーセント基準)で、これより緩いとレイアウト崩れを見逃し、これより厳しいとフォント差で常に失敗します。
落とし穴 5: Chromatic の課金プランを超過する
無料枠は月 5,000 スナップショットで、onlyChanged: true を設定していないとすぐ超過します。PR で変更がない Story まで毎回テストする必要はありません。onlyChanged: true と externals 設定(共通ファイル変更時のみ全 Story 再テスト)を組み合わせて、月間スナップショット数をコントロールします。
採用判断のフレームワーク
ここまで解説してきた構成を、すべてのプロジェクトに導入する必要はありません。私が使っている判断軸を共有します。コンポーネント数が 30 個以下の小規模プロジェクトなら Storybook 単体でドキュメント目的のみ、30〜100 個の中規模なら Chromatic を追加して Visual Regression を導入、100 個以上の大規模なら本記事の三層構造(Chromatic + Playwright + Lighthouse CI)すべてを導入する、という基準です。導入コストは小さくないので、規模に見合わない構成は逆に運用負担になります。
書籍として体系的に
まずは既存プロジェクトの中で最も使われているコンポーネント 5 つを選び、Claude Code に Story を生成させてみてください。1 時間以内に 5 つの Story と最初の Visual Regression テストが動く状態まで持っていけるはずです。そこから「これは便利だ」と感じたら本記事の三層構造を段階的に積み上げていく、というのが最も挫折しない導入ルートだと考えています。
関連記事として、Claude Code の CI 統合の基礎を整理した Claude Code Hooks 完全ガイド 2026 と、デザインシステムを Skill として配布する応用例 デザインシステムを Claude Skills として運用する も合わせてどうぞ。