畫面看起來是正常的。元件都渲染出來、沒有 console error、沒有 build warning、沒有紅色 overlay。
拿尺量才看得出問題:
- icon-only 按鈕的 computed
padding是0px,命中區只有 14×44,應該是 32×44 - 輸入框高 20px,應該是 32px
- 圖示掉回 iconify 的預設
1em(14px),而不是size-5指定的 20px
在 devtools 搜尋 runtime CSS,.p-1\.5、.size-5 這些 class 根本不存在於任何一張 stylesheet。
套件已正確安裝、module 已註冊、元件 props 都對。只有樣式是空的。
根因:一行 @import
CSS 入口只有這一行:
@import 'tailwindcss';
缺了這一行:
@import '@nuxt/ui';
Tailwind v4 拿掉了 v3 的 content: [] 設定,改成宣告式的掃描來源——@import 與 @source 指令登記哪些檔案要被掃。而 @import '@nuxt/ui' 除了帶入 theme 變數,同時把 node_modules/@nuxt/ui 的元件原始碼登記為掃描來源。
少了它,Tailwind 從來沒看過那些檔案。於是只出現在元件庫內部、而專案自己從來沒寫過的 utility(.p-1\.5、.size-5 這一類)完全不會被生成。
這裡是整件事最狡猾的地方:專案自己寫過的 class 照常生成。所以整體版面仍然成立,失效範圍剛好是「你沒寫過、但元件內部有寫」的那一批——也就是元件的內距與尺寸。症狀因此表現為局部尺寸錯亂,而不是「整站沒有樣式」,離根因非常遠。
一般化的講法是:任何宣告式內容掃描的 CSS 工具,少登記一個來源時,失效的是那個來源獨有的 utility 子集,不是全部。
為什麼沒有任何一個關卡攔得到
這才是這個坑真正值得記下來的部分:
| 關卡 | 它實際讀的東西 | 對這個坑的訊號 |
|---|---|---|
| lint | 原始碼的 AST | 零。CSS 入口的語法完全合法 |
| typecheck | TypeScript 型別 | 零。跟型別無關 |
| 元件測試(happy-dom) | DOM 結構、props、事件 | 零。happy-dom 不計算 computed style,元件照樣掛載 |
| build / CI | 有沒有錯 | 零。build 成功,沒有 warning |
| 人眼看畫面 | 版面有沒有壞 | 幾乎零。元件都在、版面成立,不拿尺量看不出來 |
四道自動關卡全綠,加上人眼看起來正常——這個缺口可以在一個專案裡活很久。
而判別成本只有一行:檢查 runtime CSS 裡有沒有 .p-1\.5。
這個落差本身就是重點。一個「所有檢查都過、但只要一行就能判定」的問題之所以能長期存在,不是因為它難,是因為沒有任何一道關卡的觀測對象是產物本身。lint 讀原始碼、typecheck 讀型別、單元測試讀 DOM 結構——沒有人讀最終那份 CSS。
怎麼判定
先確認專案真的依賴那個 UI 套件,再檢查 CSS 入口。
關鍵是不要寫死入口路徑。monorepo 的 CSS 入口可能在某個 layer 或 package 底下,要從 nuxt.config 宣告的 css 欄位去取:
# 取得真正的入口路徑,不要假設是 app/assets/css/main.css
grep -n "css:" nuxt.config.ts
# 所有 import 了 tailwindcss 但沒 import UI 套件的 CSS 檔
grep -rlE "^@import\s+['\"]tailwindcss['\"];" --include='*.css' . \
| grep -v node_modules | grep -v '/.nuxt/' \
| while read -r f; do
grep -qE "^@import\s+['\"]@nuxt/ui['\"];" "$f" || echo "AFFECTED: $f"
done
跑起來之後,決定性的驗證仍然是開 devtools 搜 runtime CSS——上面這段只是把候選縮小。
副本會自己漂掉
還有第二個面向值得一提。
同一份 CSS 入口在一個 repo 裡如果存在兩份副本(比如專案本體一份、專案樣板一份),被人維護的永遠只有本體那份。樣板那份會靜靜地漂掉,而每一個用該樣板生出來的新專案都帶著這個缺口。
副本之間沒有任何機械比對,所以漂移不會被發現——直到某個新專案的按鈕內距是 0。
這類問題的處理原則很單純:同一份設定不要有兩個維護點。做不到就補一條機械比對,讓兩份副本不一致時有東西會叫。靠人記得同步兩個地方,在時間尺度上等於沒有處理。
升級 Tailwind v4 時該一起確認的事
如果你正在做 v3 到 v4 的升級,content: [] 消失是最容易被忽略的一項改變。v3 的預設行為傾向於「掃得比較多」,v4 傾向於「你說什麼我掃什麼」。
升級之後值得逐項確認:
- 每一個第三方元件庫都有對應的
@import或@source登記 - 不在標準掃描路徑下的自家程式碼(monorepo 的其他 package、動態產生的樣板)也有登記
- 驗收看產物,不要看 build 有沒有過。開一個用到該元件庫的頁面,量一個元件的 computed style
第三點是唯一有鑑別力的。前兩點是清單,清單能幫你少漏,但只有量產物能告訴你有沒有漏。