tailwind-theme-builder
Tailwind v4 と shadcn/ui を組み合わせたテーマ付き UI をダークモード対応で構築します。依存パッケージのインストール・`@theme inline` による CSS 変数の設定・ダークモードトグルの接続・動作確認までを自動化します。ユーザーが Tailwind v4 のセットアップ、shadcn/ui のカラー設定、ダークモード、`tw-animate-css` のエラー、`@theme inline` の競合、アップグレード後の `@apply` 破損、v3 → v4 移行の問題などを言及した際に使用してください。
description の原文を見る
Set up Tailwind v4 + shadcn/ui themed UI with dark mode. Install deps, configure CSS variables via @theme inline, wire dark mode toggle, verify. Use whenever the user mentions Tailwind v4, setting up Tailwind theming, shadcn/ui colours, dark mode, or troubleshooting colours not working, tw-animate-css errors, @theme inline conflicts, @apply breaking after upgrade, or v3 → v4 migration issues.
SKILL.md 本文
Tailwind Theme Builder
ダークモード対応の完全なテーマ付き Tailwind v4 + shadcn/ui プロジェクトをセットアップします。設定済みの CSS、テーマプロバイダー、および動作するコンポーネントライブラリを生成します。
アーキテクチャ: 4 ステップパターン
Tailwind v4 では CSS 変数ベースのテーマに特定のアーキテクチャが必要です。このパターンは必須です。ステップをスキップまたは変更するとテーマが壊れます。
動作原理
CSS 変数定義 --> @theme inline マッピング --> Tailwind ユーティリティクラス
--background --> --color-background --> bg-background
(hsl() ラッパー付き) (変数を参照) (生成されるクラス)
ダークモード切り替え:
ThemeProvider が <html> の .dark クラスを切り替え
--> CSS 変数が自動的に更新される (.dark が :root をオーバーライド)
--> Tailwind ユーティリティが更新された変数を参照
--> UI が再レンダリング無しで更新される
ベストプラクティス
- セマンティック名:
--blue-500ではなく--primaryを使用 - フォアグラウンド対: すべての背景色にはフォアグラウンド対が必要 (
--primary+--primary-foreground) - WCAG コントラスト: 通常テキスト 4.5:1、大きなテキスト 3:1、UI コンポーネント 3:1
- チャート色:
@theme inlineマッピングで別の変数を使用し、スタイルプロップでvar(--chart-1)で参照
ワークフロー
ステップ 1: 依存関係をインストール
pnpm add tailwindcss @tailwindcss/vite
pnpm add -D @types/node tw-animate-css
pnpm dlx shadcn@latest init
# v3 設定ファイルが存在する場合は削除
rm -f tailwind.config.ts
ステップ 2: Vite を設定
assets/vite.config.ts をコピーするか、Tailwind プラグインを追加:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: { alias: { '@': path.resolve(__dirname, './src') } }
})
ステップ 3: 4 ステップの CSS アーキテクチャ (必須)
この順序が必須です。ステップをスキップするとテーマが壊れます。
src/index.css:
@import "tailwindcss";
@import "tw-animate-css";
/* 1. ルートで CSS 変数を定義 (@layer base の内側ではなく) */
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(221.2 83.2% 53.3%);
--primary-foreground: hsl(210 40% 98%);
/* ... すべてのセマンティックトークン */
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
--primary: hsl(217.2 91.2% 59.8%);
--primary-foreground: hsl(222.2 47.4% 11.2%);
}
/* 2. 変数を Tailwind ユーティリティにマップ */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
}
/* 3. ベーススタイルを適用 (ここに hsl() ラッパーなし) */
@layer base {
body {
background-color: var(--background);
color: var(--foreground);
}
}
結果: bg-background、text-primary などが自動的に機能します。ダークモードは .dark クラスで切り替わります。セマンティックカラーに dark: バリアントは不要です。
ステップ 4: ダークモードをセットアップ
assets/theme-provider.tsx をコンポーネントディレクトリにコピーしてアプリをラップ:
import { ThemeProvider } from '@/components/theme-provider'
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<App />
</ThemeProvider>
)
テーマ切り替えボタンを追加してください。まずドロップダウンメニューをインストール:
pnpm dlx shadcn@latest add dropdown-menu
次に ModeToggle コンポーネントを使用:
// src/components/mode-toggle.tsx
import { Moon, Sun } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { useTheme } from "@/components/theme-provider"
export function ModeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
ステップ 5: components.json を設定
{
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}
"config": "" が重要です。v4 は tailwind.config.ts を使用しません。
重要なルール
必ず:
:root/.darkの色をhsl()でラップ@theme inlineを使用してすべての CSS 変数をマップ@tailwindcss/viteプラグインを使用 (PostCSS ではなく)tailwind.config.tsが存在する場合は削除
絶対に:
:root/.darkを@layer baseの内側に配置しない.dark { @theme { } }を使用しない (v4 はネストされた @theme をサポートしていません)- ダブルラップしない:
hsl(var(--background)) @applyを@layer baseクラスで使用しない (@utilityを代わりに使用)
18 個の落とし穴すべて
クイック診断
| # | 症状 | 原因 | 修正 |
|---|---|---|---|
| 1 | 変数が無視される / テーマが壊れている | :root が @layer base の内側 | :root と .dark をルートレベルに移動 |
| 2 | ダークモード色が切り替わらない | .dark { @theme { } } を使用 | CSS 変数 + 単一の @theme inline を使用 |
| 3 | 色がすべて黒/白 | ダブル hsl() ラップ | hsl(var(...)) ではなく var(--background) を使用 |
| 4 | bg-primary が生成されない | tailwind.config.ts に色がある | 設定を削除し、@theme inline を使用 |
| 5 | bg-background クラスが見つからない | @theme inline ブロックがない | @theme inline マッピング変数を追加 |
| 6 | shadcn コンポーネントが壊れる | components.json に設定パスがある | "config": "" (空文字列) に設定 |
| 7 | Tailwind が処理されない | PostCSS プラグインを使用 | @tailwindcss/vite プラグインに切り替え |
| 8 | @/ インポートが失敗する | パスエイリアスが見つからない | tsconfig.app.json に paths を追加 |
| 9 | 冗長な dark: バリアント | dark:bg-primary-dark を使用 | bg-primary だけを使用。変数が対応 |
| 10 | ハードコードされた色が至るところに | bg-blue-600 dark:bg-blue-400 を使用 | セマンティックトークンを使用: bg-primary |
| 11 | クラスマージバグ | クラスを文字列連結 | @/lib/utils の cn() を使用 |
| 12 | Radix Select がクラッシュ | 空文字列値 value="" | value="placeholder" を使用 |
| 13 | 間違った Tailwind バージョン | tailwindcss@^3 をインストール | tailwindcss@^4.1.0 + @tailwindcss/vite をインストール |
| 14 | ピア依存関係が見つからない | tailwindcss だけをインストール | clsx、tailwind-merge、@types/node もインストール |
| 15 | ダークモードで壊れている | ライトモードだけをテスト | ライト、ダーク、システムモード、トグル遷移をテスト |
| 16 | WCAG コントラスト失敗 | 視覚的には問題ないように見える | 比率をチェック: 4.5:1 (通常テキスト)、3:1 (大き/UI) |
| 17 | アニメーションインポートでビルド失敗 | tailwindcss-animate (非推奨) を使用 | tw-animate-css またはネイティブ CSS アニメーションを使用 |
| 18 | CSS 優先度の問題 | shadcn init 後の重複 @layer base | 単一の @layer base ブロックにマージ |
落とし穴の詳細とコード例
#1 -- @layer base 内の :root
Tailwind v4 は @theme/@layer 外の CSS をストリップしますが、:root はルートレベルにある必要があります。最も一般的なセットアップエラーです。
間違い:
@layer base {
:root { --background: hsl(0 0% 100%); }
}
正解:
:root { --background: hsl(0 0% 100%); }
@layer base {
body { background-color: var(--background); }
}
#2 -- ネストされた @theme
Tailwind v4 はセレクタ内の @theme をサポートしていません。:root/.dark で CSS 変数を使用し、単一の @theme inline ブロックを使用してください。
間違い:
@theme { --color-primary: hsl(0 0% 0%); }
.dark { @theme { --color-primary: hsl(0 0% 100%); } }
正解:
:root { --primary: hsl(0 0% 0%); }
.dark { --primary: hsl(0 0% 100%); }
@theme inline { --color-primary: var(--primary); }
#3 -- ダブル hsl() ラップ
変数には既に hsl() が含まれています。ダブルラップすると hsl(hsl(...)) になります。
間違い: background-color: hsl(var(--background));
正解: background-color: var(--background);
#4 -- tailwind.config.ts の色
Tailwind v4 は設定ファイルの theme.extend.colors を完全に無視します。ファイルを削除するか空のままにしてください。components.json で "config": "" を設定してください。
#5 -- @theme inline がない
@theme inline がないと、Tailwind は CSS 変数を認識しません。bg-background などのユーティリティクラスは生成されません。
間違い:
:root { --background: hsl(0 0% 100%); }
/* @theme inline ブロックなし -- bg-background は存在しない */
正解:
:root { --background: hsl(0 0% 100%); }
@theme inline { --color-background: var(--background); }
#7 -- PostCSS vs Vite プラグイン
間違い:
export default defineConfig({
css: { postcss: './postcss.config.js' } // 古い v3 の方法
})
正解:
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()] // v4 の方法
})
#8 -- パスエイリアス
tsconfig.app.json に追加:
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@/*": ["./src/*"] }
}
}
#11 -- クラスマージの cn() ユーティリティ
間違い: className={`base ${isActive && 'active'}`}
正解: className={cn("base", isActive && "active")}
@/lib/utils の cn() は Tailwind クラスを適切にマージおよび重複排除します。
#12 -- Radix Select の空値
Radix UI Select は空文字列値を許可しません。value="" の代わりに value="placeholder" を使用してください。
#14 -- 必須の依存関係
{
"dependencies": {
"tailwindcss": "^4.1.0",
"@tailwindcss/vite": "^4.1.0",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
"@types/node": "^24.0.0"
}
}
#17 -- tw-animate-css
tailwindcss-animate は Tailwind v4 で非推奨です。shadcn/ui ドキュメントはまだそれを参照しているかもしれません。ビルド失敗とインポートエラーが発生します。tw-animate-css または @tailwindcss/motion を使用してください。
#18 -- shadcn init 後の重複 @layer base
shadcn init は独自の @layer base ブロックを追加します。初期化直後に src/index.css をチェックし、重複ブロックを 1 つに統合してください。
間違い:
@layer base { body { background-color: var(--background); } }
@layer base { * { border-color: hsl(var(--border)); } } /* shadcn から重複 */
正解:
@layer base {
* { border-color: var(--border); }
body { background-color: var(--background); color: var(--foreground); }
}
防止チェックリスト
-
tailwind.config.tsファイルがない (または空) -
components.jsonに"config": ""がある - すべての色が
:rootでhsl()ラップされている -
@theme inlineがすべての変数をマップしている -
@layer baseが:rootをラップしていない - テーマプロバイダーがアプリをラップしている
- ライト、ダーク、システムモードでテスト済み
- すべてのテキストに十分なコントラストがある
ダークモード テストチェックリスト
- ライトモードが正しく表示される
- ダークモードが正しく表示される
- システムモードが OS 設定を尊重する
- ページ更新後もテーマが保持される
- トグルコンポーネントが現在の状態を表示する
- すべてのテキストに適切なコントラストがある
- 読み込み時に間違ったテーマのフラッシュがない
- シークレットモードで機能 (グレースフルフォールバック)
アセットファイル
assets/ ディレクトリからコピー:
index.css-- すべてのカラー変数を含む完全な CSScomponents.json-- shadcn/ui v4 設定vite.config.ts-- Vite + Tailwind プラグインtheme-provider.tsx-- ダークモードプロバイダーutils.ts--cn()ユーティリティ
リファレンスファイル
references/migration-guide.md-- v3 から v4 への移行ガイド
公式ドキュメント
- shadcn/ui Tailwind v4 ガイド: https://ui.shadcn.com/docs/tailwind-v4
- shadcn/ui ダークモード (Vite): https://ui.shadcn.com/docs/dark-mode/vite
- shadcn/ui テーマ: https://ui.shadcn.com/docs/theming
- Tailwind v4 ドキュメント: https://tailwindcss.com/docs
- Tailwind ダークモード: https://tailwindcss.com/docs/dark-mode
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- jezweb
- リポジトリ
- jezweb/claude-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/jezweb/claude-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。