Tailwind CSS を v3 から v4 に上げると動かなくなること
Tailwind CSS を v3 から v4 に上げると、PostCSS プラグインと CLI が別パッケージに移り、@tailwind の 3 行と tailwind.config.js は黙って無視される。
v3 と v4 で同じコマンドを打ち比べた 8 つの変更点と、それぞれの直し方をまとめる。
公開:
v3 のプロジェクトをそのまま残し、tailwindcss だけを 4 に上げたコピーを横に置いて、同じコマンドを打ち比べた。
各節の出力は「v3 では」「v4 に上げると」「v4 で直すと」の 3 段で並ぶ。
8 つのうち、エラーで止まるのは PostCSS プラグイン・CLI・別ファイルの @apply の 3 つだけである。
残りの 5 つはエラーを出さずに CSS の中身だけが変わる。
使った HTML は次の 1 枚で、tailwind.config.js には theme.extend.colors.brand に #0ea5e9 を足してある。
<div class="text-3xl font-bold bg-brand shadow-sm ring hover:underline">
<div class="bg-black bg-opacity-50 flex-grow">overlay</div>
</div>公式の自動移行ツール npx @tailwindcss/upgrade もある。
このページは、手でバージョンだけを上げたときに何が起きるかを示す。
PostCSS プラグインが @tailwindcss/postcss に移った
v3 では tailwindcss パッケージ自体が PostCSS プラグインだった。
v4 ではプラグインが @tailwindcss/postcss に分かれ、postcss.config.js に tailwindcss: {} と書いたままだとビルドが止まる。
v3 では
$ npx postcss src/input.css -o out.css
$ echo $?
0v4 に上げると
$ npx postcss src/input.css -o out.css
Error: It looks like you're trying to use `tailwindcss` directly as a PostCSS plugin. The PostCSS plugin has moved to a separate package, so to continue using Tailwind CSS with PostCSS you'll need to install `@tailwindcss/postcss` and update your PostCSS configuration.
at mt (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:38:1643)
at LazyResult.runOnRoot (/tmp/tmp.6HSafRR78B/v4/node_modules/postcss/lib/lazy-result.js:367:16)
at LazyResult.runAsync (/tmp/tmp.6HSafRR78B/v4/node_modules/postcss/lib/lazy-result.js:296:26)
at LazyResult.async (/tmp/tmp.6HSafRR78B/v4/node_modules/postcss/lib/lazy-result.js:198:30)
at LazyResult.then (/tmp/tmp.6HSafRR78B/v4/node_modules/postcss/lib/lazy-result.js:455:17)
at file:///tmp/tmp.6HSafRR78B/v4/node_modules/postcss-cli/index.js:255:10
at async Promise.all (index 0)
$ echo $?
1v4 で直すと
$ npm install -D @tailwindcss/postcss@4
added 17 packages, and audited 70 packages in 2s
18 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities$ cat postcss.config.js
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
},
};$ npx postcss src/input.css -o out.css
$ echo $?
0@tailwindcss/postcss を入れ、postcss.config.js のプラグイン名を書き換える。
v4 は @import の展開とベンダープレフィックスを自分で行うので、postcss-import と autoprefixer を入れていたら外してよい。
@tailwind の 3 行が何も出力しなくなった
PostCSS の設定を直しても、CSS が @tailwind base; などの 3 行のままだとクラスが 1 つも出力されない。
エラーも警告も出ず、終了コードは 0 になる。
画面からスタイルが全部消えて初めて気付く変更である。
v3 では
$ cat src/input.css
@tailwind base;
@tailwind components;
@tailwind utilities;$ npx postcss src/input.css -o out.css$ grep -c text-3xl out.css
1v4 に上げると
$ cat src/input.css
@tailwind base;
@tailwind components;
@tailwind utilities;$ npx postcss src/input.css -o out.css
$ echo $?
0$ grep -c text-3xl out.css
0v4 で直すと
$ cat src/input.css
@import "tailwindcss";$ npx postcss src/input.css -o out.css
$ echo $?
0$ grep -c text-3xl out.css
53 行を @import "tailwindcss"; の 1 行に置き換える。
CLI が @tailwindcss/cli に移った
v4 の tailwindcss パッケージは CLI の実行ファイルを持たない。npx tailwindcss は実行するものを見つけられず、npm のエラーで終わる。
v3 では
$ npx tailwindcss -i src/input.css -o cli.css
Rebuilding...
Done in 92ms.
$ echo $?
0v4 に上げると
$ npx tailwindcss -i src/input.css -o cli.css
npm error could not determine executable to run
npm error A complete log of this run can be found in: /root/.npm/_logs/2026-10-30T10_41_21_125Z-debug-0.log
$ echo $?
1v4 で直すと
$ npm install -D @tailwindcss/cli@4
added 7 packages, and audited 77 packages in 1s
20 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
npm warn install-scripts 1 package has install scripts not yet covered by allowScripts:
npm warn install-scripts @parcel/watcher@2.5.1 (install: node scripts/build-from-source.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.$ npx @tailwindcss/cli -i src/input.css -o cli.css
≈ tailwindcss v4.3.3
Done in 29ms
$ echo $?
0@tailwindcss/cli を入れ、ビルドのコマンドを npx @tailwindcss/cli に変える。package.json の scripts に tailwindcss -i ... と書いている場合も同じように直す。
tailwind.config.js が読まれなくなった
v4 は tailwind.config.js を自動では探さない。
ファイルが残っていても読まれず、theme.extend で足した色やフォントのクラスは生成されない。
ここでもエラーは出ず、bg-brand だけが CSS から消える。
v3 では
$ npx postcss src/input.css -o out.css$ grep -c bg-brand out.css
1v4 に上げると
$ npx postcss src/input.css -o out.css$ grep -c bg-brand out.css
0v4 で直すと
$ cat src/input.css
@import "tailwindcss";
@config "../tailwind.config.js";$ npx postcss src/input.css -o out.css$ grep -c bg-brand out.css
1$ cat src/input.css
@import "tailwindcss";
@theme {
--color-brand: #0ea5e9;
}$ npx postcss src/input.css -o out.css$ grep -c bg-brand out.css
1直し方は 2 通りある。
今の設定ファイルを使い続けるなら、CSS に @config "../tailwind.config.js"; を書いて読み込ませる。
パスは CSS ファイルから見た相対パスになる。
v4 の書き方に移すなら、色を @theme の中に --color-brand として書く。
どちらでも bg-brand が生成された。corePlugins・safelist・separator の 3 つは @config で読み込んでも v4 では使えない。
bg-opacity-50 などの非推奨クラスが消えた
v3 で非推奨だったクラスのうち、bg-opacity-* などの不透明度のクラスは v4 で生成されなくなった。
HTML にクラスが残っていても CSS に規則が無いので、背景は不透明のまま表示される。
v3 では
$ grep -o -E '\.(bg-opacity-50|flex-grow)' out.css
.flex-grow
.bg-opacity-50v4 に上げると
$ grep -o -E '\.(bg-opacity-50|flex-grow)' out.css
.flex-growv4 で直すと
$ grep -n overlay src/index.html
2: <div class="bg-black/50 grow">overlay</div>$ npx postcss src/input.css -o out.css$ grep -o -E '\.(bg-black\\/50|grow) ' out.css
.grow
.bg-black\/50色の後ろに /50 を付ける書き方に変える。bg-black bg-opacity-50 は bg-black/50 になる。
移行ガイドの削除一覧には flex-grow-* も載っているが、4.3.3 では flex-grow はまだ生成された。
いずれ消える前提で grow に書き換えておくと、次の更新で同じ確認をしなくて済む。
shadow-sm と ring は名前が同じまま見た目が変わった
v4 は影・角丸・ぼかしの段階を 1 つずつずらした。
v3 の shadow-sm は v4 の shadow-xs に当たり、v4 の shadow-sm は v3 の shadow と同じ太さになる。ring の幅も 3px から 1px に変わった。
クラスはどれも生成されるので、エラーにも空振りにもならず、見た目だけが変わる。
v3 では
$ grep -A 4 -E '^\.shadow-sm \{' out.css
.shadow-sm {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}$ grep -A 4 -E '^\.ring \{' out.css
.ring {
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}v4 に上げると
$ grep -A 3 -E '^ \.shadow-sm \{' out.css
.shadow-sm {
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}$ grep -A 3 -E '^ \.ring \{' out.css
.ring {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}v4 で直すと
$ grep -n text-3xl src/index.html
1:<div class="text-3xl font-bold bg-brand shadow-xs ring-3 hover:underline">$ npx postcss src/input.css -o out.css$ grep -A 3 -E '^ \.shadow-xs \{' out.css
.shadow-xs {
--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}$ grep -A 3 -E '^ \.ring-3 \{' out.css
.ring-3 {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}v3 と同じ見た目に戻すには、shadow-sm を shadow-xs に、ring を ring-3 に書き換える。
同じずれは rounded・blur・drop-shadow・backdrop-blur にもあり、outline-none は outline-hidden に名前が変わった。
別ファイルの @apply には @reference が要る
Vue や Svelte の <style>、CSS Modules のように、メインの CSS とは別に処理される CSS は、v4 では Tailwind のクラスを知らない。
そこで @apply を使うと、知らないクラスとしてビルドが止まる。
v3 では同じファイルがそのまま通っていた。
v3 では
$ cat src/button.css
.btn {
@apply px-4 font-bold;
}$ npx postcss src/button.css -o button.out.css
$ echo $?
0$ cat button.out.css
.btn {
padding-left: 1rem;
padding-right: 1rem;
font-weight: 700
}v4 に上げると
$ cat src/button.css
.btn {
@apply px-4 font-bold;
}$ npx postcss src/button.css -o button.out.css
Error: Cannot apply unknown utility class `px-4`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive
at onInvalidCandidate (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:21:1682)
at Oe (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:16:39796)
at /tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:21:711
at ai (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:5:1775)
at O (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:5:1377)
at Pe (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:21:172)
at On (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:38:294)
at async _n (/tmp/tmp.6HSafRR78B/v4/node_modules/tailwindcss/dist/lib.js:38:631)
at async an (/tmp/tmp.6HSafRR78B/v4/node_modules/@tailwindcss/node/dist/index.js:10:3415)
at async E (/tmp/tmp.6HSafRR78B/v4/node_modules/@tailwindcss/postcss/dist/index.js:10:3996)
CssSyntaxError: tailwindcss: /tmp/tmp.6HSafRR78B/v4/src/button.css:1:1: Cannot apply unknown utility class `px-4`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive
> 1 | .btn {
| ^
2 | @apply px-4 font-bold;
3 | }
$ echo $?
1v4 で直すと
$ cat src/button.css
@reference "tailwindcss";
.btn {
@apply px-4 font-bold;
}$ npx postcss src/button.css -o button.out.css
$ echo $?
0$ cat button.out.css
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
.btn {
padding-inline: calc(var(--spacing, 0.25rem) * 4);
--tw-font-weight: var(--font-weight-bold, 700);
font-weight: var(--font-weight-bold, 700);
}
@property --tw-font-weight {
syntax: "*";
inherits: false;
}
@layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop {
--tw-font-weight: initial;
}
}
}ファイルの先頭に @reference "tailwindcss"; を書く。@theme で足した色などを @apply で使うときは、"tailwindcss" の代わりにそれを書いたメインの CSS のパスを指定する。@reference は定義を読むだけで、Tailwind の CSS を出力に重ねて含めることはない。
hover: がホバーできる端末でしか効かなくなった
v4 の hover: は @media (hover: hover) の中に出力される。
マウスのある端末では v3 と同じに見えるが、スマートフォンではタップしても hover: のスタイルが付かない。
タップで色が変わることを前提にしたメニューやボタンは、v4 に上げると反応しなくなる。
v3 では
$ grep -c "@media (hover: hover)" out.css
0v4 に上げると
$ grep -c "@media (hover: hover)" out.css
1$ grep -B 1 -A 3 -E 'hover\\:underline' out.css
@media (hover: hover) {
.hover\:underline:hover {
text-decoration-line: underline;
}
}v4 で直すと
$ cat src/input.css
@import "tailwindcss";
@config "../tailwind.config.js";
@custom-variant hover (&:hover);$ npx postcss src/input.css -o out.css$ grep -c "@media (hover: hover)" out.css
0v3 と同じ挙動に戻すなら、CSS に @custom-variant hover (&:hover); を足す。
タップで開くメニューのように、ホバーできないと使えない作りなら、クリックやフォーカスで開くように作り直すほうが端末を選ばない。
検証環境Node.js 24.22.2 / npm 11.19.1 / Python 3.11.2 / 2026-10-30 検証(ほか 6 件)
- 検証日
- 実行環境
node:24Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 24.22.2
- npm 11.19.1
- Python 3.11.2
- Git 2.39.5
- Tailwind CSS(旧) 3.4.19
- Tailwind CSS(新) 4.3.3
- PostCSS 8.5.32
- @tailwindcss/postcss 4.3.3
- @tailwindcss/cli 4.3.3
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。