Tailwind CSS v4は「CSS-First」に全面移行し、JavaScriptの設定ファイル(tailwind.config.js)が廃止されました。デザイントークンはすべてCSSの@themeディレクティブで定義し、カスタムユーティリティも@utilityで作成します。
この変化はClaude Codeとの相性を大幅に高めました。CSSファイルを読むだけでデザインシステム全体を把握でき、コンポーネント生成時に正しいトークンを自動で使ってくれます。この記事では、Tailwind CSS v4 + shadcn/ui v4 + Claude Codeでデザインシステムを構築する実践的な方法を解説します。
Tailwind CSS v4 の主要変更点
| v3 | v4 |
|---|---|
tailwind.config.js でデザイントークン定義 |
@theme {} でCSS内に定義 |
@tailwind base/components/utilities |
@import "tailwindcss" の1行 |
darkMode: 'class' |
@custom-variant dark (...) |
| rgb/hslカラー | OKLCHカラー(広色域P3対応) |
| プラグインでコンテナクエリ対応 | コアに統合(プラグイン不要) |
| JavaScript設定の静的解析 | Rustエンジン(Oxide)で2〜5倍高速ビルド |
v3→v4の自動移行
# 自動移行ツールで一括変換 npx @tailwindcss/upgrade # 手動で必要なもの: # 1. tailwind.config.jsの内容をglobals.cssの@themeに移す # 2. darkModeの設定を@custom-variantに移す # 3. tailwindcss-animateをtw-animate-cssに置換
@themeディレクティブでデザイントークンを定義する
v4では、@themeブロック内にCSS変数を定義するだけで、対応するユーティリティクラスが自動生成されます。
globals.css(デザイントークン定義)
@import "tailwindcss";
/* デフォルトテーマをリセットして独自トークンのみ定義する場合 */
@theme {
/* カラー: --color-* で定義 → bg-primary, text-primary 等が使える */
--color-primary-50: oklch(0.97 0.02 250);
--color-primary-100: oklch(0.93 0.04 250);
--color-primary-500: oklch(0.60 0.16 250);
--color-primary-600: oklch(0.53 0.16 250);
--color-primary-900: oklch(0.25 0.08 250);
--color-gray-50: oklch(0.98 0 0);
--color-gray-100: oklch(0.96 0 0);
--color-gray-500: oklch(0.55 0 0);
--color-gray-900: oklch(0.15 0 0);
/* フォント: --font-* → font-sans, font-mono が使える */
--font-sans: "Inter", "Noto Sans JP", sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* 角丸: --radius-* → rounded-sm, rounded-md 等が使える */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-xl: 1rem;
/* シャドウ */
--shadow-sm: 0 1px 2px oklch(0 0 0 / 0.05);
--shadow-md: 0 4px 6px oklch(0 0 0 / 0.07);
}
--color-primary-500を定義するだけで、bg-primary-500・text-primary-500・border-primary-500等のユーティリティクラスが自動生成されます。v3のようにconfig.jsに書く必要はありません。3層トークンアーキテクチャ ── Base・Semantic・Component
実務でのデザインシステムでは、デザイントークンを3層に分けて管理するのが効果的です。
| レイヤー | 用途 | 例 |
|---|---|---|
| Base(プリミティブ) | 生の値 | --color-blue-500: oklch(0.60 0.16 250) |
| Semantic(セマンティック) | 意図を表す | --color-action-primary: var(--color-blue-600) |
| Component(コンポーネント) | 部品固有 | --button-bg: var(--color-action-primary) |
3層トークンの実装例(globals.css)
/* Layer 1: Base(プリミティブ) */
:root {
--color-blue-500: oklch(0.60 0.16 250);
--color-blue-600: oklch(0.53 0.16 250);
--color-gray-50: oklch(0.98 0 0);
--color-gray-900: oklch(0.15 0 0);
}
/* Layer 2: Semantic(意図ベース) */
:root {
--background: var(--color-gray-50);
--foreground: var(--color-gray-900);
--primary: var(--color-blue-600);
--primary-foreground: oklch(0.98 0 0);
--destructive: oklch(0.55 0.22 25);
}
.dark {
--background: var(--color-gray-900);
--foreground: var(--color-gray-50);
--primary: oklch(0.65 0.16 250);
}
/* Layer 3: Tailwindユーティリティに接続 */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-destructive: var(--destructive);
}
@theme inlineを使うと、var()の参照先をTailwindが正しく解決してユーティリティクラスを生成します。セマンティックトークンをTailwindに接続する場合はinlineを付けてください。ダークモード実装パターン
v4ではダークモードの設定方法が変わりました。用途に応じて3パターンから選びます。
| パターン | 設定 | 用途 |
|---|---|---|
| media(デフォルト) | 設定不要 | OS設定に自動追従 |
| class | @custom-variant dark |
ユーザーが手動切替 |
| data属性 | @custom-variant dark |
マルチテーマ対応 |
class戦略(ユーザー切替対応)
/* globals.css */ @custom-variant dark (&:where(.dark, .dark *)); /* これで dark:bg-gray-900 等が .dark クラスの子要素で有効になる */
ダークモードトグル(React)
import { useEffect, useState } from "react";
export function ThemeToggle() {
const [dark, setDark] = useState(false);
useEffect(() => {
// 初期値: OS設定 or localStorage
const saved = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
setDark(saved === "dark" || (!saved && prefersDark));
}, []);
useEffect(() => {
document.documentElement.classList.toggle("dark", dark);
localStorage.setItem("theme", dark ? "dark" : "light");
}, [dark]);
return (
<button onClick={() => setDark((d) => !d)}>
{dark ? "☀️ ライト" : "? ダーク"}
</button>
);
}
shadcn/ui CLI v4 × Claude Code連携
shadcn/ui CLI v4(2026年3月)には、Claude Code向けの新機能が追加されました。
shadcn Skillsで正確なコンポーネント生成
shadcn Skillsのインストール
# Claude Codeにshadcn/uiの知識を追加 pnpm dlx skills add shadcn/ui # → Claude Codeがshadcn/uiの最新API・バリアント・プロパティを # 正確に把握した状態でコンポーネントを生成できるようになる
プリセットでデザイン設定を一括投入
shadcn/ui プリセット活用
# プリセットで初期設定を一括投入 pnpm dlx shadcn@latest init --preset a1Dg5eFl # 変更を事前確認(ファイル変更なし) pnpm dlx shadcn@latest add dialog --dry-run # 差分を確認 pnpm dlx shadcn@latest add dialog --diff # コンポーネントのドキュメントを取得 pnpm dlx shadcn@latest docs combobox
コンポーネント生成をClaude Codeに依頼する
コンポーネント生成プロンプト
UserProfileCardコンポーネントを作成してください。 要件: - shadcn/ui Card をベースに - Avatar(shadcn/ui)で丸アバター表示 - 名前(font-bold)、メールアドレス、ロールバッジ(Badge) - ダークモード対応(dark: プレフィックス使用) - レスポンシブ: モバイルは縦積み、md以上で横並び - loading時のSkeleton状態も含める - TypeScript型定義付き - デザイントークン(globals.css)の変数を使用すること
生成されるコンポーネント例
import { Card, CardContent } from "@/components/ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
interface UserProfileCardProps {
user?: {
name: string;
email: string;
avatarUrl: string;
role: "admin" | "member" | "viewer";
};
loading?: boolean;
}
const roleBadgeVariant = {
admin: "destructive",
member: "default",
viewer: "secondary",
} as const;
export function UserProfileCard({ user, loading }: UserProfileCardProps) {
if (loading) {
return (
<Card>
<CardContent className="flex flex-col md:flex-row items-center gap-4 p-6">
<Skeleton className="h-16 w-16 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-48" />
</div>
</CardContent>
</Card>
);
}
if (!user) return null;
return (
<Card>
<CardContent className="flex flex-col md:flex-row items-center gap-4 p-6">
<Avatar className="h-16 w-16">
<AvatarImage src={user.avatarUrl} alt={user.name} />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="text-center md:text-left">
<p className="font-bold text-foreground">{user.name}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
<Badge variant={roleBadgeVariant[user.role]} className="mt-1">
{user.role}
</Badge>
</div>
</CardContent>
</Card>
);
}
CLAUDE.mdテンプレート(デザインシステム用)
CLAUDE.md(Tailwind v4 デザインシステム)
## デザインシステム ### テクノロジースタック - Tailwind CSS v4(CSS-First。tailwind.config.jsは使わない) - shadcn/ui(Radix UIベース。CLIでインストール。直接編集禁止) - Lucide React(アイコン) ### カラー - プライマリ: primary-500〜primary-900(globals.cssの@theme定義を使う) - グレースケール: gray-50〜gray-900 - すべてOKLCH形式で定義 - ハードコードされた色(#ff0000等)は禁止。必ずデザイントークンを使う ### スペーシング - コンポーネント間: gap-4 (16px) 以上 - セクション間: py-16 (64px) 以上 - 任意値(pl-[17px]等)は禁止。デフォルトスケールから選ぶ ### タイポグラフィ - Sans: Inter + Noto Sans JP - Mono: JetBrains Mono - 見出し: font-bold tracking-tight - 本文: text-base leading-relaxed ### コンポーネントルール - 新規UI部品はshadcn/uiを先に検討。なければTailwindで自作 - dark:対応は必須(すべてのコンポーネント) - inline style禁止 - !important禁止 - className: clsx() または cn() で条件付きクラスを結合 ### 命名規約 - ファイル: kebab-case (user-profile-card.tsx) - コンポーネント: PascalCase (UserProfileCard) - Props: TypeScript interface定義必須
CLAUDE.mdの詳しい書き方はCLAUDE.md完全ガイドをご覧ください。
@utilityと@custom-variantでカスタム拡張する
v4ではカスタムユーティリティとバリアントをCSS内で直接定義できます。
@utilityの定義例
/* カスタムユーティリティ: hover:やdark:等全バリアントが自動適用される */
@utility scrollbar-hidden {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
/* 使い方: <div class="scrollbar-hidden hover:scrollbar-auto"> */
@utility text-balance {
text-wrap: balance;
}
/* 使い方: <h1 class="text-balance"> */
@custom-variantの定義例
/* ダークモード(class戦略) */ @custom-variant dark (&:where(.dark, .dark *)); /* RTL対応 */ @custom-variant rtl (&:where([dir="rtl"] *)); /* 使い方: <div class="rtl:text-right"> */ /* 印刷時のスタイル */ @custom-variant print (@media print); /* 使い方: <div class="print:hidden"> */
@utilityで定義したクラスには、hover:・focus:・dark:・md:等すべてのバリアントが自動で使えます。v3のプラグインAPIより大幅に簡潔です。よくある質問
Qv3からv4への移行は大変ですか?
A
npx @tailwindcss/upgradeで大部分は自動変換されます。手動で対応が必要なのは、tailwind.config.jsの内容をCSSの@themeに移すこと、darkMode: 'class'を@custom-variant darkに変更すること、tailwindcss-animateをtw-animate-cssに置換することの3点です。Claude Codeに「このプロジェクトのTailwindをv3からv4に移行して」と依頼すると一括対応できます。Qglobals.cssの@themeとshadcn/uiのCSS変数はどう共存させますか?
Ashadcn/uiのCSS変数(
--background、--primary等)は:rootと.darkで定義し、@theme inlineでTailwindユーティリティに接続します。inlineキーワードによりvar()の参照先が正しく解決され、bg-background・text-primary等が使えるようになります。QOKLCHカラーとは何ですか?従来のrgb/hslとの違いは?
AOKLCHはLightness(明度)・Chroma(彩度)・Hue(色相)の3軸で色を表現する形式で、人間の知覚に基づいた均一な色空間です。rgb/hslでは明度を均等に変えても見た目が均一にならない問題がありましたが、OKLCHではLightness値を均等に配置すると知覚的に一貫したカラースケールが作れます。また、P3広色域をサポートしているためsRGB以上の鮮やかな色が表現できます。
QClaude Codeがv3の記法(tailwind.config.js等)でコードを生成します
ACLAUDE.mdに「Tailwind CSS v4を使用。tailwind.config.jsは使わない。@theme/CSS変数でデザイントークンを定義」と明記してください。さらにshadcn Skillsをインストールすると、v4のAPIが正確に参照されるようになります。
Qshadcn/uiのコンポーネントをカスタマイズしてもよいですか?
Ashadcn/uiのコンポーネントは
components/ui/にコピーされるため変更可能ですが、CLIで再追加した際にカスタマイズが上書きされるリスクがあります。カスタマイズが必要な場合は、shadcn/uiコンポーネントをラップする独自コンポーネントを作る方法がおすすめです。CLAUDE.mdに「shadcn/uiコンポーネントの直接編集禁止。ラッパーコンポーネントで拡張する」と書いておきましょう。まとめ
- Tailwind CSS v4はCSS-First:
@themeでデザイントークンを定義、@utility/@custom-variantでカスタム拡張 - 3層トークンアーキテクチャ: Base→Semantic→Componentの3層でトークンを管理し、
@theme inlineでTailwindに接続 - ダークモード:
@custom-variant darkでclass戦略に対応。セマンティック変数を.darkで上書き - shadcn/ui CLI v4: Skills/Presets/dry-runの新機能でClaude Codeとの連携が向上
- CLAUDE.mdにデザインルールを明記: カラー・スペーシング・タイポグラフィのルールを書いておくとClaude Codeが一貫したUIを生成する
フロントエンド開発全般はClaude Codeフロントエンド開発ガイド、Next.jsとの統合はClaude Code × Next.js完全ガイドもあわせてご覧ください。

