CSS @layer 怎麼排?沒分層的樣式為什麼總是贏
驗證於
本文目錄
按鈕寫了 class="btn p-4",畫面上的 padding 卻是 0。打開 DevTools,.p-4 的規則被劃掉了,贏的是 CSS 檔裡一行很普通的 .btn { padding: 0; }。兩者都只有一個 class,specificity 相同,決定勝負的是 layer。
這是 Tailwind CSS v4 專案常見的狀況。v4 改用瀏覽器原生的 cascade layers,utility 全部放在 @layer utilities 裡(Tailwind CSS 升級指南);而自己寫、沒包進任何 layer 的 .btn,依 cascade 規則會贏過所有分層的樣式。
用 Tailwind CSS v4.3.5 編譯、在 Chrome 157 實測:
| 寫法 | 按鈕的 padding-top |
|---|---|
.btn { padding: 0; } 直接寫在 @import "tailwindcss"; 後面 | 0px |
改成 @layer components { .btn { padding: 0; } } | 16px |
修法就是把自訂元件樣式放回 @layer components,讓 utility 層可以覆寫它。要理解為什麼,得先看 @layer 的排序規則。
@layer 的三條規則
MDN 的 @layer 說明寫得很完整,實際會影響除錯的是這三條:
- 層的先後比 specificity 優先。 後宣告的層贏,即使它的選擇器比較簡單。
- 沒分層的樣式排在所有層之後。 不管寫在檔案的哪個位置,一般宣告都會贏過任何 layer 裡的宣告。
!important會把順序反過來。 越早宣告的層,!important越強;分層的!important也會贏過沒分層的!important。
下面這段可以直接存成 HTML,用瀏覽器打開,對照每個元素的計算結果:
<style> @layer reset, components, utilities;
@layer components { .card .title { color: green; } } @layer utilities { .text-red { color: red; } }
p { margin: 3px; } @layer reset { p { margin: 0; } }
@layer components { .imp { color: green !important; } } @layer utilities { .imp { color: red !important; } }</style>
<div class="card"><h2 class="title text-red">紅色</h2></div><p>margin 是 3px</p><span class="imp">綠色</span>- 標題是紅色:
.text-red只有一個 class,仍然贏過兩個 class 的.card .title,因為utilities層較晚宣告。 - 段落的 margin 是 3px:沒分層的
p寫在@layer reset前面,照樣勝出。 .imp是綠色:兩邊都是!important,較早的components反而贏。
第三條最容易讓人誤會。如果團隊習慣用 !important 臨時壓過樣式,導入 layer 之後要先清點這些地方,因為它的勝負方向和一般宣告相反。
既有專案怎麼排層
第一行先用一個 @layer 陳述式把順序定下來。順序以第一次出現為準,之後在其他檔案追加同名的層,不會改變它的位置。
@layer reset, vendor, legacy, components, utilities;
@import url("vendor/datepicker.css") layer(vendor);@import url("legacy.css") layer(legacy);@import 必須放在其他規則前面,只有 @charset 和 @layer 陳述式可以排在它之前。
我會這樣分配:
reset:重置與全站基礎樣式,越早越好,任何人都能覆寫。vendor:第三方套件的 CSS。放進 layer 之後,它的選擇器寫得再長,自家元件樣式都能用簡單的 class 蓋過去,不必再拚 specificity。legacy:還沒整理的舊樣式,下面會說明為什麼需要這一層。components:自家元件。utilities:單一用途的輔助 class,排最後,讓它在 HTML 上永遠有最終決定權。
實測時,第三方的 .dropdown .item { padding: 12px; } 放進 vendor 層後,元素上的單一 class .p-1 就能把 padding 改成 4px。
導入時最需要注意的,是還沒搬進 layer 的舊樣式會一下子變成最強的那一層。邊搬邊上線的話,舊 CSS 會壓過剛搬進 components 的新寫法,看起來像是搬壞了。所以我會先把整份舊 CSS 整包放進上面的 legacy 層,再逐步把規則搬到正確的層。legacy 排在 vendor 後面,舊程式碼原本對第三方套件的覆寫仍然有效;排在 components 前面,新寫的元件樣式從第一天起就能蓋過它。
Tailwind CSS v4 自己宣告了 theme, base, components, utilities 四層。用 v4 時,自訂元件寫進 @layer components,想讓 hover:、md: 這些 variant 套用到自訂 class,則改用 @utility。v3 的 @layer 是由 Tailwind 在編譯時搬移規則,不是瀏覽器的 cascade layers,從 v3 升級時要特別留意這個差異。
值不值得導入
**值得:**專案裡有第三方 CSS、正在用 Tailwind CSS v4,或已經出現為了蓋過別人而一路加長選擇器、加 !important 的情況。layer 能把「誰該贏」寫成一行明確的宣告,比靠檔案順序和 specificity 猜測好維護。
**可以先不做:**小型網站、樣式全部自己寫、也沒有覆寫衝突的話,導入 layer 主要是多一層概念要記,收益不大。
@layer 自 2022 年 3 月起在各主要瀏覽器可用,MDN 標為 Baseline 廣泛可用,相容性通常不是考量重點。真正的成本在團隊理解:「沒分層的樣式最強」和「!important 反轉」這兩條規則,要讓每個會改 CSS 的人都知道,否則下一次除錯時又會回到加長選擇器的老路。
本文範例在 2026-11-14 於 macOS 上的 Chrome 157(headless)執行,以 getComputedStyle() 讀取結果;Tailwind 的情境使用 @tailwindcss/cli 4.3.5 編譯後測試。
參考資料: