層疊、繼承與優先權(Cascade & Specificity)
同一元素被多條規則打到時,瀏覽器不是「隨便選一條」,而是依 層疊(Cascade) 決定勝負:來源 → 層 → 優先權 → 出現順序。搞懂這件事,就不用到處灑 !important。
繼承(Inheritance)
有些屬性會從父元素傳到子元素,例如 color、font-family、line-height。margin、padding、border、width 不會繼承。
article {
color: #222;
font-family: "Noto Sans TC", sans-serif;
}
article h2 {
/* 文字顏色與字體跟著 article,不必重寫 */
margin-top: 1.5em; /* margin 不繼承,要自己設 */
}
強制控制繼承:
| 關鍵字 | 意義 |
|---|---|
inherit |
用父元素的計算值 |
initial |
該屬性的初始值 |
unset |
可繼承屬性當 inherit,否則當 initial |
revert |
回到瀏覽器/使用者樣式 |
層疊(Cascade)比的是什麼
由高到低大致是:
- 來源:瀏覽器預設 < 使用者樣式 < 作者樣式(你寫的 CSS)
!important:作者的!important通常壓過普通規則(使用者!important更強,這裡少遇)@layer順序:越晚定義的 layer 越強;未進 layer 的作者樣式更強- 優先權(specificity)
- 原始順序:權重相同時,後面的贏
優先權(Specificity)怎麼算
習慣寫成 (inline, id, class, type) 四個數字,由左往右比,不是十進位相加。
| 來源 | 權重 | 例子 |
|---|---|---|
行內 style="" |
(1, 0, 0, 0) |
style="color: black" |
| ID | (0, 1, 0, 0) |
#header |
| class、屬性、偽類 | (0, 0, 1, 0) |
.title、[type]、:hover |
| 元素、偽元素 | (0, 0, 0, 1) |
h1、::before |
萬用、:where()、組合符號 |
(0, 0, 0, 0) |
*、>、:where(h1) |
:not()、:is()、:has() 的權重,取括號裡權重最高的那項;:where() 永遠是 0。
| 選擇器 | 權重 | 誰贏 |
|---|---|---|
p |
(0, 0, 0, 1) |
最低 |
.title |
(0, 0, 1, 0) |
贏過元素 |
p.title |
(0, 0, 1, 1) |
贏過單獨 .title |
#hero |
(0, 1, 0, 0) |
贏過 class |
#hero.title |
(0, 1, 1, 0) |
更高 |
| 行內 style | (1, 0, 0, 0) |
一般規則幾乎蓋不掉 |
#main h1 { color: red; } /* (0, 1, 0, 1) */
h1.title { color: blue; } /* (0, 0, 1, 1) */
/* h1 會是紅色:ID 那一欄已經比較大 */
!important
它會跳過普通優先權比較,讓除錯變困難。合理用途只有:
- 覆蓋第三方套件且暫時抽不出更精準的選擇器
- 工具 class(例如
.hidden { display: none !important; })
不要用 !important 修選擇器戰爭
正確做法是降低選擇器權重(多用 class、少用 id)、或把公用樣式放進 @layer。
@layer(建議專案一開始就用)
把「重置、主題、元件」分開,後面的 layer 覆蓋前面的,不必靠更長的選擇器:
@layer reset, tokens, components, utilities;
@layer tokens {
:root { --text: #222; }
}
@layer components {
.btn { padding: 0.5rem 1rem; }
}
@layer utilities {
.mt-0 { margin-top: 0; }
}
未放入任何 layer 的作者 CSS,會蓋過所有 layer。這讓「偶爾的頁面覆寫」仍然好寫。
實務原則
- 元件樣式用 單一 class(
.card、.btn),不要div.wrapper > ul > li > a - 狀態用修飾 class:
.btn.is-active,權重只多一個 class - 需要「很容易被蓋」的預設時用
:where() - 權重打平時,把覆寫寫在後面,或抽成更後面的 layer
總結
- 可繼承的多半是文字相關屬性;盒子相關屬性要自己設。
- 勝負順序:來源 /
!important→@layer→ specificity → 先後。 - 行內 > ID > class > 元素;
:where()權重為 0。 !important是最後手段。
下一步進入 盒模型,看元素實際佔掉多少空間。