TailwindCSS v4 遷移指南:從 v3 升級的破壞性變更、@tailwindcss/upgrade 自動遷移與漸進策略完整教學 | TailwindCSS 完整教學

2026/08/06
TailwindCSS v4 遷移指南:從 v3 升級的破壞性變更、@tailwindcss/upgrade 自動遷移與漸進策略完整教學 | TailwindCSS 完整教學

TailwindCSS v4 是一次根本性的架構重設計:設定從 JavaScript 搬進 CSS、核心引擎用 Rust 重寫、@tailwind 三行換成一行 @import,還有一整批 utility 被系統性重新命名。這些破壞性變更(breaking changes)讓從 v3 直接升級不是「換個版本號」那麼簡單。這篇是 TailwindCSS 完整教學 系列的第六篇,我們把前幾篇散落的 v3/v4 差異收攏起來,帶你走一遍完整的 v4 遷移 流程——從自動遷移工具 npx @tailwindcss/upgrade、逐項破壞性變更對照,到大型專案的漸進升級策略。

前言

遷移(migration) 指的是把一個既有專案從 TailwindCSS v3 升級到 v4 的完整過程——不只是更新套件版本,還包含改寫設定檔、替換棄用語法、修復預設值變化,以及驗證畫面沒有跑掉。TailwindCSS v4 的變更幅度是歷來最大的一次,所以「遷移」在這裡是一件需要方法的事,而不是 npm update 一鍵完成。

用一個現實世界的類比來理解:把 v3 升級到 v4,就像搬家到一間格局大改的新房子。地址(TailwindCSS)沒變、你的家具(HTML 結構、大部分 class)也大多能帶過去,但新房子的電路配置改了(設定從 JS 搬到 CSS)、開關位置換了@tailwind 換成 @import)、有幾件家具的尺寸標籤重貼過shadow-sm 現在叫 shadow-xs),而且新房子只收現代的插頭(需要較新的瀏覽器)。搬家公司(@tailwindcss/upgrade)能幫你把大件家具搬過去、貼好新標籤,但有些精細的東西——牆上的畫該掛哪、燈光色溫對不對——還是得你親自到現場比對。這篇就是你的搬家清單。

本篇你將學到:

  • 四大破壞性變更:設定檔改 CSS-first、@tailwind@import、utility 系統性重命名、工具鏈與預設值變化,一張總覽表看懂全貌
  • 自動遷移工具npx @tailwindcss/upgrade 幫你做什麼、做不到什麼,以及執行前後的注意事項
  • 常見遷移坑:邊框變黑、focus ring 變細、陰影偏移這些「升級後畫面跑掉」的真兇與解法
  • 漸進遷移策略:大型專案如何用 @config 相容指令分階段遷移,把風險攤平

本系列以 TailwindCSS v4 為預設版本,本篇會明確標註每一項與 v3 的差異。

核心概念

v3 → v4 四大變更區塊

在動手之前,先建立一張全景地圖。v4 的破壞性變更可以歸納成四個區塊:配置指令utility 命名工具鏈與預設值。下面這張表把每個區塊的核心變化並列出來:

區塊v3 做法v4 做法
配置tailwind.config.js(JS 物件)CSS 裡的 @theme { }
內容偵測content: [...] 手動列出自動偵測(免設定)
外掛plugins: [require(...)]@plugin "..."
自訂 Tokentheme.extend.colors.brand--color-brand: ...
指令@tailwind base/components/utilities@import "tailwindcss"
theme() 語法theme('colors.blue.500')var(--color-blue-500)theme(--color-blue-500)
screen()screen('md')theme(--breakpoint-md)
utility 命名shadow-smblur-smrounded-smshadow-xsblur-xsrounded-xs(整體 shift)
漸層bg-gradient-to-rbg-linear-to-r
透明度bg-opacity-50bg-black/50(斜線語法)
工具鏈tailwindcss(PostCSS 外掛)@tailwindcss/postcss
autoprefixer需要不再需要(Lightning CSS 內建)
Vite 整合透過 PostCSS@tailwindcss/vite(原生)

這張表最需要先建立的認知是:v4 的變更不是零散的、而是有一條主軸——「CSS-first」。前面《配置系統》與《指令與函式》兩篇其實已經在鋪這條路:設定搬進 CSS(@theme)、擴充能力搬進 CSS(@utility@custom-variant)。這一篇的遷移,本質上就是把一個「以 JS 為配置中心」的專案,翻譯成「以 CSS 為配置中心」的專案。理解了這條主軸,很多變更就不再是需要死背的規則,而是同一個方向的必然結果。

值得多花一點篇幅說明「為什麼要做這麼大的架構轉移」,因為理解動機能讓你在遷移時心裡有底、少踩坑。v3 的 JS 配置有三個結構性的痛點。第一,JS 與 CSS 之間有一道牆。 tailwind.config.js 裡定義的 Token 會被拿去生成 utility class,但這些值沒辦法直接在你手寫的 CSS 規則裡引用——你得透過 theme() 函式這座橋才能取用。v4 把 Token 本身變成真正的 CSS 變數,var(--color-brand) 直接就能用,牆被拆掉了。第二,Plugin API 太重。 v3 要擴充一個 utility 或變體,得學 addUtilities()addComponents()matchUtilities()addVariant() 這一整套 JS API;v4 多數場景改用 @utility@custom-variant 在 CSS 裡幾行就搞定。第三,認知分裂。 設計 Token 在 JS 檔、樣式在 CSS 檔,你在瀏覽器 DevTools 看到的是 CSS 變數,但要搞懂它從哪來卻得跳回去翻 JS 設定。v4 讓 Token 就定義在 CSS 裡,DevTools 直接可見,這條追查鏈變短了。

把這三點合起來看,v4 的方向其實是「盡量貼近原生 CSS 標準」。隨著瀏覽器原生的 @layer、CSS 變數、@property@scope、CSS Nesting 逐一成熟,Tailwind 能把越來越多原本要靠自家引擎硬幹的事,交還給瀏覽器本身。這也是為什麼 v4 敢設下較高的瀏覽器門檻——它是在賭「未來」,用現代 CSS 特性換取更透明、更少魔法的架構。你在遷移時遇到的每一個破壞性變更,幾乎都能回溯到這個「向 CSS 標準靠攏」的初衷。

破壞性變更詳解:三種「會讓畫面跑掉」的變化

四大區塊裡,配置與指令的變更是「不改就 build 不起來」,錯得明顯、容易發現;真正陰險的是那些「build 成功、但畫面悄悄變了」的破壞性變更。這類問題分三種,值得單獨拆開講。

第一種:utility 系統性重命名(shift down 模式)。 v4 對陰影、模糊、圓角做了系統性的名稱位移——原本的 sm 縮小為 xs,騰出 sm 給視覺上更合理的尺度:

v3 classv4 class視覺效果
shadow-smshadow-xs相同(只是改名)
shadowshadow-sm相同
shadow-md / -lg / -xl不變相同
blur-smblur-xs相同
blurblur-sm相同
rounded-smrounded-xs相同
roundedrounded-sm相同
ringring-3明確化為 3px(見下)

關鍵在於:視覺效果沒變,是名稱平移了。所以如果你升級後沒改名,shadow-sm 會突然套用到「原本 v3 shadow 的較大陰影」,設計稿對齊的陰影就會整批偏移。

為什麼 v4 要做這種看似多餘的改名?背後的設計考量是「讓尺度命名更一致、更符合直覺」。在 v3,sm(small)其實是最小的一級,但直覺上「small」應該還有比它更小的空間;同時 shadow(無後綴)這種「裸名」在整個尺度系統裡也顯得突兀——它到底該排在 sm 前面還是後面,沒有明說。v4 藉這次大版本一次把它理順:引入 xs(extra-small)當最小級,讓 smmdlgxl 形成一條前後一致、沒有裸名的完整尺度。代價就是這次遷移得整批改名,但換來的是往後新增尺度時不會再有命名上的尷尬。這也是為什麼這類改名值得交給 @tailwindcss/upgrade 自動處理——它是純機械性的字串替換,人工做既枯燥又容易漏。

第二種:預設值變更。 這是最容易被忽略、也最讓人抓狂的一類,因為 class 名稱一字沒改,但預設行為變了:

項目v3 預設v4 預設影響
border(不指定色)gray-200currentColor邊框顏色整批改變
ring 寬度3px1pxfocus ring 變細
ring 顏色blue-500/50(半透明藍)currentColorfocus ring 變成文字色

也就是說,一個 v3 寫著 <div class="border"> 的元素,在 v4 邊框顏色會從淺灰變成「繼承自文字的深色」;一個 <input class="focus:ring"> 的輸入框,focus 時的光暈會從 3px 藍色變成 1px 深色。這些都不會報錯,只會在你回頭看畫面時覺得「怪怪的」。

第三種:語法層級的破壞性變更。 這些是遷移時最容易漏掉的細節,因為它們藏在任意值與修飾子的寫法裡:

項目v3v4
CSS 變數任意值bg-[--brand]bg-(--brand)
任意值逗號分隔grid-cols-[max-content,auto]grid-cols-[max-content_auto](改空格)
important 位置!flex(前綴)flex!(後綴)
variant 堆疊順序右到左 first:*:pt-0左到右 *:first:pt-0
關閉 outlineoutline-noneoutline-hidden

還有兩個容易遺漏的行為變更:其一,v4 的 hover: 只在支援 hover 的裝置生效(包在 @media (hover: hover) 裡),解決觸控裝置的「黏著 hover」問題,但也意味著你若依賴 hover 在觸控裝置生效的設計要重新檢視;其二,v4 不再支援 Sass / Less / Stylus——因為 v4 本身就是預處理器,不再設計與其他 CSS 前處理器搭配使用。

瀏覽器需求:v4 的入場門檻

v4 之所以能把架構做得這麼「CSS 原生」,代價是它大量依賴現代 CSS 特性(Cascade Layers、@propertycolor-mix() 等)。因此 v4 有明確的最低瀏覽器需求:Safari 16.4+、Chrome 111+、Firefox 128+

這件事在遷移決策上很重要:如果你的產品目標瀏覽器矩陣落在這個範圍內(絕大多數 2023 年後的瀏覽器都符合),儘管升級;但若你仍需支援更舊的環境,官方建議繼續使用 v3.4,它會持續維護。換句話說,遷移的第一步不是動手改 code,而是先確認「我的使用者用的瀏覽器版本,v4 吃得下嗎」。

實務上怎麼確認?最可靠的方法是打開你的網站分析(例如 Google Analytics 或 Search Console)的瀏覽器與版本分佈報表,看看實際造訪者的瀏覽器版本落點。如果 v4 不支援的舊瀏覽器佔比極低(例如低於千分之一),那多半可以接受;但如果你的受眾包含大量企業內網、政府單位或使用老舊裝置的族群,這個比例可能高得驚人,貿然升級 v4 會讓這部分使用者看到破版的畫面。這也是為什麼「先看數據再決定遷移時機」比「跟風升級最新版」更負責任——版本號不是越新越好,適合你的使用者才是關鍵。

實作範例

理論看完,我們來實際走一遍遷移。以下先示範自動遷移工具,再看逐步的手動遷移前後對照。

首選:npx @tailwindcss/upgrade 自動遷移

官方的升級 CLI 是遷移的第一站,多數專案跑完它就完成八成的工作。執行前務必先開一個乾淨的 git branch,方便事後比對 diff:

# 前置需求:Node.js 20 以上
# 建議先切到乾淨的 branch
git checkout -b chore/upgrade-tailwind-v4

# 執行官方自動遷移工具
npx @tailwindcss/upgrade

這支工具會自動幫你處理以下項目:

# @tailwindcss/upgrade 自動處理的項目:
#   @tailwind 三行            → @import "tailwindcss"
#   tailwind.config.js        → CSS 的 @theme(基本轉換)
#   postcss.config.js         → @tailwindcss/postcss
#   utility 重命名            → shadow-sm→shadow-xs、blur-sm→blur-xs …
#   theme() 語法              → 點記法字串改為 CSS 變數名
#   screen('md')             → theme(--breakpoint-md)

# 仍需你「手動收尾」的項目:
#   複雜的 JS 外掛邏輯
#   含字串插值的動態 theme() 用法
#   JS 配置中用到函式/運算的部分
#   預設值變更(border 顏色、ring 寬度)的「視覺」修復

跑完之後,一定要人工檢查 diff、並實際打開畫面比對——工具能改語法,但改不動那些「語法對、但視覺變了」的預設值差異。

手動遷移前後對照

若你想理解工具到底做了什麼,或有些地方需要手動處理,下面是核心檔案的 v3 → v4 對照。

步驟一:更新依賴。 先卸載 v3 相關套件,裝上 v4:

# 移除 v3
npm uninstall tailwindcss autoprefixer

# 安裝 v4
npm install tailwindcss@latest

# 依你的建構工具三選一:
npm install @tailwindcss/vite     # Vite 專案(推薦,效能最好)
npm install @tailwindcss/postcss  # PostCSS / Webpack / Next.js
npm install @tailwindcss/cli      # 零依賴 CLI

步驟二:更新 CSS 入口。 這是最核心、也最不能漏的一步:

/* ❌ Before(v3)*/
@tailwind base;
@tailwind components;
@tailwind utilities;

/* ✅ After(v4):一行取代三行 */
@import "tailwindcss";

步驟三:把 tailwind.config.js 搬進 @theme。 v3 的 JS 設定物件,在 v4 變成 CSS 裡的 @theme 區塊,每個 Token 都是一個 CSS 變數:

/* ✅ After(v4):src/index.css */
@import "tailwindcss";

/* 外掛:對應 v3 的 plugins: [require(...)] */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";

/* 自訂 Token:對應 v3 的 theme.extend */
@theme {
  /* 顏色:theme.extend.colors.brand → --color-brand-* */
  --color-brand-50:  #eff6ff;
  --color-brand-500: #3b82f6;
  --color-brand-950: #172554;

  /* 間距:theme.extend.spacing.18 → --spacing-18 */
  --spacing-18: 4.5rem;

  /* 字體:theme.extend.fontFamily.display → --font-family-display */
  --font-family-display: "Cal Sans", "Inter", sans-serif;

  /* 斷點:注意 v4 斷點用 rem,不用 px(480px ÷ 16 = 30rem)*/
  --breakpoint-xs:  30rem;
  --breakpoint-3xl: 112rem;
}

/* Dark mode:對應 v3 的 darkMode: 'class' */
@custom-variant dark (&:where(.dark, .dark *));

步驟四:更新 PostCSS 設定(若用 PostCSS)。套件名稱換了,autoprefixer 也可以移除:

/* ❌ Before(v3):postcss.config.js */
module.exports = {
  plugins: {
    tailwindcss: {},   // 舊套件名稱
    autoprefixer: {},  // v4 不再需要
  },
}

/* ✅ After(v4)*/
module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},  // 新套件名稱,Lightning CSS 已內建 prefix
  },
}

或者,改用效能更好的 Vite 原生外掛:

/* vite.config.js */
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [tailwindcss()],
});

驗證遷移完整性

改完之後,用幾條 grep 快速掃出殘留的 v3 語法,確認沒有漏網之魚:

# 應該都「查無結果」——若有結果代表還沒改乾淨
grep -r "@tailwind "  src/       # 應改為 @import "tailwindcss"
grep -r "screen('"    src/       # 應改為 theme(--breakpoint-*)
grep -r "overflow-ellipsis" src/ # 應改為 text-ellipsis
grep -r "flex-grow"   src/       # 應改為 grow

# 這些需要「人工確認」是否要改名(視覺 shift)
grep -r "shadow-sm"   src/       # 確認是否應改為 shadow-xs
grep -r "blur-sm"     src/       # 確認是否應改為 blur-xs
grep -r "rounded-sm"  src/       # 確認是否應改為 rounded-xs

常見錯誤與最佳實踐

五個最容易踩的遷移坑

坑一:@tailwind 指令無效、樣式完全不生效。 這是最常見的第一個障礙。症狀是 CSS 出現 @tailwind 警告、或整個網站沒有任何 Tailwind 樣式。原因是 v4 已移除 @tailwind 指令,解法是把三行換成一行 @import "tailwindcss"

坑二:Cannot find module 'tailwindcss'(build 失敗)。 這是第二個障礙。原因是 postcss.config.js 還用舊的外掛名稱 tailwindcss。解法是改成 @tailwindcss/postcss 並確認已 npm install @tailwindcss/postcss

坑三:邊框顏色意外變深。 這是「預設值變更」的頭號受害者。症狀是 <div class="border"> 的邊框從淺灰變成黑色或深色,原因是 v4 的 border 預設色從 gray-200 改為 currentColor。有兩種修法:

/* 方案 A:在每個 border 後明確指定顏色 */
/* v3: <div class="border">  →  v4: <div class="border border-gray-200"> */

/* 方案 B:在 @layer base 全域還原 v3 行為 */
@layer base {
  *, ::before, ::after {
    --tw-default-border-color: var(--color-gray-200);
  }
}

坑四:focus ring 變細、變色。 症狀是表單 focus 樣式從 3px 藍色光暈變成 1px 深色細線,原因是 ring 預設寬度從 3px → 1px、顏色從 blue-500/50currentColor。修法:

/* 逐一修:v3 <input class="focus:ring">
          → v4 <input class="focus:ring-3 focus:ring-blue-500/50"> */

/* 或全域還原 */
@layer base {
  :root {
    --tw-ring-color: var(--color-blue-500) / 50%;
  }
}

坑五:陰影大小整批偏移。 症狀是設計稿對齊的陰影看起來「大了一號」,原因就是前面說的 shift 模式——v4 的 shadow-sm 對應的是 v3 的 shadow。解法是系統性替換:shadow-smshadow-xsshadowshadow-smshadow-md 以上不變),blurrounded 同理。這一步交給 @tailwindcss/upgrade 最省事。

大型專案的漸進遷移策略

小專案可以一次全部升級,但超過上百個元件的大型專案,一口氣改完風險太高。這時可以用 @config 相容指令把遷移拆成四個階段,讓風險攤平:

/* Phase 1 的關鍵:用 @config 讓「v4 引擎」暫時吃「v3 設定」*/
@import "tailwindcss";
@config "./tailwind.config.js";  /* 暫時保留舊設定,之後再刪 */

四階段策略如下:

  • Phase 1 — 工具鏈切換(1–2 天):更新依賴、CSS 入口換 @import,並用 @config 指令暫時保留 tailwind.config.js。目標是「v4 引擎 + v3 設定」能並存、build 不報錯。
  • Phase 2 — 配置遷移(2–5 天):把 theme.extend 逐項轉成 @themeplugins 轉成 @plugin,測試每個 Token 的 utility 是否正確生成,完成後刪掉 tailwind.config.js@config
  • Phase 3 — utility 重命名(自動化,0.5 天):跑 npx @tailwindcss/upgrade,手動確認 shadow / blur / rounded 的視覺,並修復 border / ring 預設值。
  • Phase 4 — 全面測試(2–5 天):視覺回歸測試(逐頁截圖比對)、互動狀態測試(hover、focus、dark mode)、跨瀏覽器測試。

最佳實踐清單

  • 先確認瀏覽器需求:目標矩陣不在 Safari 16.4+ / Chrome 111+ / Firefox 128+ 範圍內,就留在 v3.4。
  • 一律先跑 @tailwindcss/upgrade:在乾淨 branch 上執行,讓工具做掉八成的機械性替換。
  • 重點盯「預設值變更」:border、ring 這類工具改不動的視覺差異,一定要人工比對畫面。
  • 大型專案用 @config 分階段:先讓 v4 引擎吃 v3 設定,再逐步搬家,別一次改完。
  • 收尾一定做視覺回歸測試:用 Playwright、Chromatic 或 Percy 截圖比對,抓出肉眼容易漏的偏移。

小結

這是 TailwindCSS 完整教學 系列的第六篇,也是 TW-1「核心概念」段落的收尾。上一篇《指令與函式》我們拆解了 v4 在 CSS 裡的完整語彙——@import@layer@apply@utility@custom-varianttheme()--alpha()--spacing();這一篇則把前面幾篇散落的 v3/v4 差異全部收攏,實際走了一遍從 v3 升級到 v4 的完整流程。回顧幾個重點:

  • 四大變更區塊:配置改 CSS-first(@theme)、指令換 @import、utility 系統性重命名(shift 模式)、工具鏈換 @tailwindcss/postcss——一條「CSS-first」主軸貫穿全部。
  • 三種會讓畫面跑掉的變更:utility 重命名、預設值變更(border 變 currentColor、ring 變 1px)、語法層級變更(任意值括號、important 後綴)——其中「預設值變更」最陰險,因為它不報錯。
  • 自動遷移工具npx @tailwindcss/upgrade(需 Node.js 20+)做掉八成機械性工作,但預設值的視覺修復必須人工收尾。
  • 瀏覽器需求:v4 需要 Safari 16.4+ / Chrome 111+ / Firefox 128+,需支援更舊環境就續用 v3.4。
  • 漸進策略:大型專案用 @config 相容指令,把遷移拆成工具鏈、配置、重命名、測試四個階段。

到這裡,TW-1 核心概念系列——從 Utility-First 哲學、安裝設定、配置系統、指令與函式,到這篇 v4 遷移——已經完整鋪好了 v4 的地基。下一篇 《TailwindCSS 排版與文字》,我們會正式進入 utility 的實戰,從最日常的字型、字級、行高、字距與文字對齊開始,帶你把設計系統的 Token 真正用在畫面上。

BenZ Software Developer

熱愛技術的軟體開發者,在這裡分享程式開發經驗與學習筆記。

本週主打

AI 自動化入門包

你每天手動在做的那些煩事,其實 AI 可以自己跑。這份給你 10 個照著做就會的自動化工作流 + 50 個複製即用的提示詞,不用會寫程式。

看看這個產品 →