Claude Media
Claude CodeでTailwind v4へ移行する — upgradeツール実行後の直し方

Claude CodeでTailwind v4へ移行する — upgradeツール実行後の直し方

Tailwind CSS v3からv4への移行を、公式のupgradeツールを先に走らせ、残りをClaude Codeで直す順に書きます。CLAUDE.mdに固定する規約と検証ループも載せました。

先に公式のupgradeツールを走らせる

Tailwind CSS v3からv4への移行は、Claude Codeに全部を書き換えさせるより、公式のアップグレードツールを先に実行してから残りをClaude Codeで直す順が安全です。移行ガイドは、ツールが依存関係の更新、設定ファイルのCSSへの移行、テンプレートファイルの変更まで自動で扱うと説明しています。

npx @tailwindcss/upgrade

実行前の条件が3つあります。

  • ツールはNode.js 20以上を要求します
  • 新しいブランチで実行し、差分を確認してからブラウザで動作を見るよう、ガイドが勧めています
  • v4はSafari 16.4以上、Chrome 111以上、Firefox 128以上が対象です。それより古いブラウザをサポートするなら、v3.4に留まる前提になります

ガイドは、複雑なプロジェクトでは手作業の調整が残りうること、ツールが拾わない変更に備えて変更点の一覧にも目を通すことを勧めています。

最後の条件はClaude Codeに任せる前に人間が決める部分です。サポート対象のブラウザが古いなら、移行そのものを見送る判断になります。

手順

Claude Codeに任せる移行の流れ

  1. 1

    ブランチを切る

    専用ブランチで作業し、ツールの実行前に未コミットの変更を残さないようにします。

  2. 2

    upgradeツールを実行する

    npx @tailwindcss/upgrade を走らせます。手編集の前にこの1コマンドを済ませると、機械的な置き換えと判断が要る修正が混ざりません。

  3. 3

    ツールの差分だけをコミットする

    ここまでを1コミットにすると、後の手直しと切り分けて見直せます。

  4. 4

    ビルドと見た目を検証する

    ビルドの終了コードと、画面の見た目の両方で確認します。

  5. 5

    残った箇所を直す

    次の章の一覧を使って、ツールが変えなかった箇所を探して直します。

移行ルールをCLAUDE.mdに固定する

Claude CodeはCLAUDE.mdをコンテキストとして読みます。強制される設定ではないので、検証できる具体的な書き方が効きます。公式は「Run npm test before committing」のように、確認できる形で書くよう勧めています。移行の場合は、手順の順序と、新規に書かないクラスを並べます。

# Tailwind v4 移行ルール
- 最初に `npx @tailwindcss/upgrade` を実行する。手編集はその後
- upgradeツールの差分は単独でコミットする
- 変更のたびに `npm run build` と `npm run lint` を実行し、結果を示す
- 新規に書かない: `flex-shrink-*` `flex-grow-*` `bg-opacity-*` `outline-none`
- `@tailwind` ディレクティブは使わず `@import "tailwindcss"` を使う
- border・ring・placeholderの既定値は、独断で戻さず一覧で報告する

ビルドとlintのコマンド名は例です。プロジェクトの実際のスクリプト名に置き換えます。長さは200行未満を目標にし、移行が終わったらこの節ごと消します。移行専用の規約は、終われば古くなる情報だからです。

CLAUDE.mdの置き場所や読み込みの仕組みは、CLAUDE.mdにテストコマンドを書くVitestの例と同じです。検証コマンドを固定する考え方も共通しています。

検証をClaude Codeに回させる

公式のベストプラクティスは、Claudeが自分で実行できる確認手段を渡すことを最優先に挙げています。確認手段はテスト、ビルドの終了コード、リンター、画面のスクリーンショットなどです。確認手段がないと、「できたように見える」ことが唯一の合図になり、人間が検証ループの代わりになります。

Tailwindの移行で渡せる確認手段は2つです。

確認手段拾えるもの拾えないもの
ビルドの終了コード拾えるもの構文エラー、設定ファイルの読み込み失敗拾えないもの見た目の差
lint拾えるもの残った旧クラス名(ルールを設定している場合)拾えないもの既定値の変更による色や幅の差
スクリーンショットの比較拾えるもの枠線色、リングの太さ、余白の差拾えないもの画面に出ない状態の差

見た目の差はビルドを通ります。最後の行が、移行で一番漏れやすい部分です。移行前後の主要画面をスクリーンショットで撮っておき、比較させる依頼文にします。

ブランチ chore/tailwind-v4 で npx @tailwindcss/upgrade を実行して。
終わったら git diff --stat を見せて、変更を次の3種類に分類して:
設定のCSS化、テンプレートのクラス名変更、依存関係の更新。
その後 npm run build を実行して、失敗したら原因を直してから再実行して。

この依頼文は例で、返ってくる分類は実際の差分次第です。Claudeの要約をそのまま信じず、git diffで数行は自分の目で見ておきます。差分の正しさそのものは、同じ変更をultrareviewで別の目に見せることで補強できます。

見た目が変わる既定値の変更

upgradeツールを実行した後、ビルドが通っても画面が変わる箇所があります。移行ガイドの変更点一覧は、どれがツールの自動対応かを項目ごとには示していません。自動対応する項目が多いとだけ書かれているので、既定値の変更は実行後に自分で探す前提で動きます。

項目v3v4v3の見た目を保つ手段
枠線の既定色v3gray-200v4currentColorv3の見た目を保つ手段@layer base で border-color を指定
ring の幅と色v33px、blue-500v41px、currentColorv3の見た目を保つ手段ring-3 と色の明示、または @theme の変数
プレースホルダー色v3gray-400v4現在の文字色の50%v3の見た目を保つ手段@layer base で色を指定
ボタンのカーソルv3pointerv4defaultv3の見た目を保つ手段@layer base で cursor: pointer
dialog の余白v3ブラウザ既定v4Preflightでリセットv3の見た目を保つ手段dialog { margin: auto; }

枠線色の例は、ガイドでは次の形で示されています。

@layer base {
  *,
  ::after,
  ::before,
  ::backdrop,
  ::file-selector-button {
    border-color: var(--color-gray-200, currentColor);
  }
}

ここは判断が分かれるところです。v3の見た目を保つ互換コードを足すか、クラスに色を明示してv4の流儀に寄せるかです。ringの互換用に使う --default-ring-width などの変数について、ガイドは互換のためだけの変数で、v4の慣用的な使い方ではないと書いています。移行を長く引きずらないなら、互換コードより明示指定の方が後が楽です。

Claude Codeには、この判断を渡さずに候補を出させます。CLAUDE.mdの最後の行に「独断で戻さず一覧で報告する」と書いたのはこのためです。

ツールの後に残りやすいクラス名と構文

名前が変わったユーティリティは、grepで機械的に拾えます。ガイドの一覧から、旧名を探すコマンドを組みました。

grep -rnE "(flex-shrink|flex-grow|bg-opacity|text-opacity)-" src
grep -rnE "outline-none|@tailwind |bg-\[--|theme\(" src
grep -rnE "grid-cols-\[[^]]*," src

3行目は、ガイドが挙げるカンマ区切りの任意値の旧記法を探します。v4では空白をアンダースコアで書きます。

主な置き換え先は次のとおりです。

v3v4
shadow-sm / shadowv4shadow-xs / shadow-sm
rounded-sm / roundedv4rounded-xs / rounded-sm
blur-sm / blurv4blur-xs / blur-sm
outline-nonev4outline-hidden
ringv4ring-3
flex-shrink-* / flex-grow-*v4shrink-* / grow-*
bg-opacity-* などv4bg-black/50 のような透過の指定
bg-[--brand-color]v4bg-(--brand-color)
! を先頭に付ける重要度指定v4クラス名の末尾に !

shadow-sm と shadow は、置き換えの順序で事故が起きます。shadow-smをshadow-xsに変える前にshadowをshadow-smに変えると、両者が衝突します。Claude Codeに任せるときは「shadow-smを先にshadow-xsへ、その後にshadowをshadow-smへ」と順序を指定します。blur・rounded・drop-shadow・backdrop-blurも同じ型です。

先頭の ! による重要度指定は、ガイドによると互換のため動き続けますが非推奨です。急いで消さなくても壊れません。

クラス名が同じなのに挙動が変わる項目

grepで拾えないのが、書き方は同じで動きが変わる項目です。ガイドの一覧から、画面を触らないと気づきにくいものを選びました。

項目v4での変更影響が出やすい場面
space-x-* / space-y-* / divide-*v4での変更セレクタが :not(:last-child) に変更影響が出やすい場面インライン要素や、子要素に別の余白を足している箇所
hoverv4での変更主入力デバイスがhoverに対応するときだけ適用影響が出やすい場面タップでhover状態にしていたタッチ端末のUI
グラデーションのバリアントv4での変更一部だけ上書きしても他の値が保たれる影響が出やすい場面dark:from-* だけ変えていたグラデーション
transform-nonev4での変更rotate・scale・translate をリセットしない影響が出やすい場面フォーカス時に scale-150 を戻していた箇所
transition-[opacity,transform]v4での変更transform を指定しても個別プロパティは遷移しない影響が出やすい場面scale-* を使うアニメーション
スタックしたバリアントv4での変更適用順が右から左へ変わり、左から右になる影響が出やすい場面first:*:pt-0 のような組み合わせ
transitionv4での変更outline-color も対象になる影響が出やすい場面フォーカス時に枠色を条件付きで付けている箇所

hoverの変更に対して、ガイドは旧実装に戻すための @custom-variant hover (&:hover); を示しています。同時に、hoverは補助的な機能として扱い、動作を依存させないよう勧めています。

こうした項目はスクリーンショット比較か、実機のブラウザ操作でしか見つかりません。CLAUDE.mdに「見た目の検証はスクリーンショットで示す」と1行足しておくと、Claude Codeが比較の材料を持ってくるようになります。

構成そのものが変わる箇所

クラス名の置き換えでは済まない変更があります。ここはツールが扱わなかったときに、手で構成を直す部分です。

  • @tailwind base; などの3行は、@import "tailwindcss"; の1行になります
  • tailwind.config.js は自動で検出されなくなりました。使い続けるならCSSに @config "../../tailwind.config.js"; を書いて読み込みます
  • corePlugins・safelist・separator のオプションは非対応です。safelistの代わりは @source inline() です
  • @layer utilities や @layer components に書いた独自クラスは、@utility ディレクティブに書き換えます
  • container の center や padding の設定は消え、@utility container で拡張します
  • プレフィックスは tw:flex のようにバリアント風の形になり、クラス名の先頭に付きます。CSSでは @import "tailwindcss" prefix(tw); と書きます。テーマ変数は、プレフィックスなしの名前で定義します
  • theme(colors.red.500) は、CSS変数 var(--color-red-500) に置き換えるのが推奨です。メディアクエリ内では theme(--breakpoint-xl) の形を使います
  • v3で提供されていた resolveConfig は削除されました。JavaScriptから値が要るときは、getComputedStyle でルート要素のCSS変数を読みます
  • PostCSSの利用者は、プラグインが @tailwindcss/postcss パッケージに移ります。postcss-import と autoprefixer は不要になります
  • Viteの利用者には、PostCSSプラグインより専用の @tailwindcss/vite が勧められています
  • CLIは @tailwindcss/cli パッケージに分かれ、npx tailwindcss は npx @tailwindcss/cli になります

Vue・Svelte・CSS Modulesで @apply を使っているプロジェクトは要注意です。別にバンドルされるスタイルシートからは、他のファイルで定義したテーマ変数やカスタムユーティリティが見えなくなりました。対処は @reference "../../app.css"; で読み込むか、@apply をやめてCSS変数を直接使うかです。

Sass・Less・Stylusとの併用は、v4では想定されていません。これらでスタイルシートを書いているプロジェクトは、upgradeツールを走らせる前に、プリプロセッサを外す作業が先に要ります。ここはClaude Codeに丸投げせず、方針を人が決めます。

移行が複数パッケージにまたがるとき

モノレポで複数のアプリが同じTailwind設定を共有していると、どのパッケージを先に上げるかが問題になります。Claude Codeの1セッションに全部を載せると、差分が大きくなりレビューが追いつきません。

ツールの実行と検証はパッケージごとにセッションを分け、CLAUDE.mdの規約は共通にする進め方があります。各セッションの冒頭で /context を実行すると、移行ルールが読み込まれているか確かめられます。モノレポのCLAUDE.mdの置き方はTurborepoの例にまとまっています。バージョンをまたぐ移行を検証コマンドで区切る進め方は、Prismaスキーマ設計の記事でも扱っています。

移行が終わった後に消すもの

作業が終わったら、片付ける対象が3つあります。

  1. CLAUDE.mdの移行ルール。終わった作業の手順は、次の作業のコンテキストを圧迫するだけです
  2. 互換のために足した @layer base の上書き。見た目を保つために足したものは、クラスに色を明示できた時点で外せます
  3. tailwind.config.js を @config で読み込んでいるなら、その設定を @theme に移せないかの検討

互換コードを残したままの移行は、見た目は同じでも、v3の癖を引きずった状態です。移行のコミットと片付けのコミットを分けておくと、後から戻すのも簡単です。

この記事を共有:XはてブLinkedIn