CSS層疊簡介

層疊是一種演算法,用於定義使用者代理如何組合來自不同源的屬性值。當來自多個層疊層@scope塊中的宣告為元素設定屬性值時,層疊定義了哪個源和層具有優先權。

層疊是CSS的核心,正如其名稱所強調的:層疊樣式表。當選擇器匹配一個元素時,即使來自較低優先順序源或層的選擇器具有更高的特異度,具有最高優先順序源的屬性值也會被應用。

本文解釋了層疊是什麼以及CSS宣告的層疊順序,涵蓋了層疊層和源型別。理解源優先順序是理解層疊的關鍵。

源型別

CSS層疊演算法的任務是選擇CSS宣告,以確定CSS屬性的正確值。CSS宣告來自不同的源型別:使用者代理樣式表作者樣式表使用者樣式表

儘管樣式表來自這些不同的源,並且可以位於每個源中的不同中,但它們在預設作用域方面存在重疊;為了使之工作,層疊演算法定義了它們如何互動。在討論互動之前,我們將在接下來的幾節中定義一些關鍵術語。

使用者代理樣式表

使用者代理或瀏覽器具有基本的樣式表,為任何文件提供預設樣式。這些樣式表被稱為使用者代理樣式表。大多數瀏覽器為此目的使用實際的樣式表,而其他瀏覽器則在程式碼中模擬它們。最終結果是相同的。

有些瀏覽器允許使用者修改使用者代理樣式表,但這很少見,也無法控制。

儘管HTML規範對使用者代理樣式表設定了一些限制,但瀏覽器有很大的自由度:這意味著瀏覽器之間存在一些差異。為了簡化開發過程,Web開發人員可能會使用CSS重置樣式表,例如normalize.css,它在開始根據特定需求進行修改之前,將所有瀏覽器的常見屬性值設定為已知狀態。

除非使用者代理樣式表在屬性旁邊包含!important,使其成為“重要的”,否則作者樣式(包括重置樣式表)宣告的樣式優先於使用者代理樣式,無論相關選擇器的特異度如何。

作者樣式表

作者樣式表是最常見的樣式表型別;這些是Web開發人員編寫的樣式。如上所述,這些樣式可以重置使用者代理樣式,並定義給定網頁或應用程式設計的樣式。作者(或Web開發人員)使用一個或多個連結或匯入的樣式表、<style>塊以及使用style屬性定義的內聯樣式來定義文件的樣式。這些作者樣式定義了網站的外觀和感覺——它的主題。

使用者樣式表

在大多數瀏覽器中,網站的使用者(或讀者)可以選擇使用自定義的使用者樣式表來覆蓋樣式,以根據使用者的意願定製體驗。根據使用者代理,使用者樣式可以直接配置或透過瀏覽器擴充套件新增。

層疊層

層疊順序基於源型別。每個源型別內的層疊基於該型別內層疊層的宣告順序。對於所有源——使用者代理、作者或使用者——樣式可以在命名層或匿名層內部或外部宣告。當使用layerlayer()@layer宣告時,樣式被放置到指定的命名層中,如果未提供名稱,則放置到匿名層中。在層外宣告的樣式被視為匿名最後宣告層的一部分。

讓我們先看看層疊源型別,然後再深入探討每個源型別中的層疊層。

層疊順序

層疊演算法確定如何為每個文件元素的每個屬性找到要應用的值。以下步驟適用於層疊演算法

  1. 相關性:它首先過濾來自不同源的所有規則,只保留適用於給定元素的規則。這意味著選擇器匹配給定元素且是適當media at-rule一部分的規則。

  2. 源和重要性:然後它根據其重要性(即是否帶有!important)和其源對這些規則進行排序。暫時忽略層,層疊順序如下

    優先順序順序(從低到高) Origin 重要性
    1 使用者代理(瀏覽器) normal
    2 使用者 normal
    3 作者(開發者) normal
    4 CSS關鍵幀動畫
    5 作者(開發者) !important
    6 使用者 !important
    7 使用者代理(瀏覽器) !important
    8 CSS 過渡
  3. 特異度:在源相同的情況下,規則的特異度被考慮用於選擇一個值或另一個值。比較選擇器的特異度,特異度最高的宣告獲勝。

  4. 作用域鄰近度:當具有優先順序的源層中的兩個選擇器具有相同的特異度時,作用域規則中到作用域根的DOM層次結構跳數最少的屬性值獲勝。有關詳細資訊和示例,請參閱@scope衝突如何解決

  5. 出現順序:在具有優先順序的源中,如果屬性存在競爭值,並且這些值位於樣式塊中,匹配具有相同特異度和作用域鄰近度的選擇器,則樣式順序中最後的宣告將被應用。

層疊是升序的,這意味著

  • 動畫優先於正常值,無論是在使用者、作者還是使用者代理樣式中宣告。
  • 重要值優先於動畫,無論是在使用者、作者還是使用者代理樣式中宣告。
  • 過渡優先於重要值。

注意: 過渡和動畫

由動畫@keyframes設定的屬性值比所有正常樣式(未設定!important的樣式)更重要。

transition中設定的屬性值優先於所有其他設定值,即使是那些標記為!important的值。

層疊演算法在特異度演算法之前應用,這意味著如果使用者樣式表(第2行)中聲明瞭:root p { color: red;},而作者樣式表(第3行)中聲明瞭特異度較低的p {color: blue;},則段落將顯示為藍色。

基本示例

在深入探討層疊層如何影響層疊之前,讓我們看一個涉及不同源的多個CSS源的示例,並逐步完成層疊演算法的步驟

這裡我們有一個使用者代理樣式表,兩個作者樣式表和一個使用者樣式表,HTML中沒有內聯樣式

使用者代理 CSS

css
li {
  margin-left: 10px;
}

作者 CSS 1

css
li {
  margin-left: 0;
} /* This is a reset */

作者 CSS 2

css
@media screen {
  li {
    margin-left: 3px;
  }
}

@media print {
  li {
    margin-left: 1px;
  }
}

@layer namedLayer {
  li {
    margin-left: 5px;
  }
}

使用者 CSS

css
.specific {
  margin-left: 1em;
}

HTML

html
<ul>
  <li class="specific">1<sup>st</sup></li>
  <li>2<sup>nd</sup></li>
</ul>

在這種情況下,li.specific規則內的宣告應該適用。

層疊演算法再次有五個步驟,按順序排列

  1. 相關性
  2. 源和重要性
  3. 優先順序
  4. 作用域鄰近度
  5. 出現順序

1px用於列印媒體。由於其媒體型別缺乏相關性,它被排除在考慮之外。

沒有宣告標記為!important,因此優先順序順序是作者樣式表高於使用者樣式表高於使用者代理樣式表。根據源和重要性,使用者樣式表中的1em和使用者代理樣式表中的10px被排除在考慮之外。

請注意,即使使用者樣式中.specific上的1em具有更高的特異度,它也是使用者樣式表中的一個普通宣告。因此,它的優先順序低於任何作者樣式,並在演算法的源和重要性步驟中被移除,甚至在特異度發揮作用之前。

作者樣式表中有三個宣告

css
li {
  margin-left: 0;
} /* from author css 1 */
css
@media screen {
  li {
    margin-left: 3px;
  }
}
css
@layer namedLayer {
  li {
    margin-left: 5px;
  }
}

最後一個,5px是層疊層的一部分。層中的正常宣告的優先順序低於相同源型別中不在層中的正常樣式。這也透過演算法的第2步,源和重要性,被移除。

這留下了03px,它們都具有相同的選擇器,因此具有相同的特異度。它們都不在@scope塊中,因此在此示例中作用域鄰近度也不起作用。

然後我們檢視出現順序。第二個,即兩個未分層作者樣式中最後一個,獲勝。

css
margin-left: 3px;

注意: 使用者CSS中定義的宣告,即使它可能具有更高的特異度,也不會被選擇,因為層疊演算法的源和重要性特異度演算法之前應用。在層疊層中定義的宣告,即使它可能在程式碼中出現較晚,也不會優先,因為層疊層中的正常樣式優先順序低於正常的未分層樣式。出現順序僅在源、重要性和特異度都相等時才重要。

作者樣式:內聯樣式、層和優先順序

層疊順序表提供了優先順序順序概述。該表將使用者代理、使用者和作者源型別樣式總結為每行兩行,分別是“源型別 - 正常”和“源型別 - !important”。每個源型別內的優先順序更為細微。樣式可以包含在其源型別內的層中,對於作者樣式,還存在內聯樣式在層疊順序中的位置問題。

宣告層的順序在確定優先順序方面很重要。層中的正常樣式優先於在先前層中宣告的樣式;而未在任何層中宣告的正常樣式優先於正常分層樣式,無論特異度如何。

在此示例中,作者使用CSS的@import規則在<style>資訊元素中匯入了五個外部樣式表。

html
<style>
  @import "unlayeredStyles.css";
  @import "AStyles.css" layer(A);
  @import "moreUnlayeredStyles.css";
  @import "BStyles.css" layer(B);
  @import "CStyles.css" layer(C);
  p {
    color: red;
    padding: 1em !important;
  }
</style>

然後在文件正文中我們有內聯樣式

html
<p style="line-height: 1.6em; text-decoration: overline !important;">Hello</p>

在上面的CSS程式碼塊中,按順序建立了名為“A”、“B”和“C”的三個層疊層。三個樣式表被直接匯入到層中,兩個在沒有建立或分配到層的情況下匯入。下面列表中的“所有未分層樣式”(正常作者樣式優先順序 - 順序4)包括來自這兩個樣式表和額外的未分層CSS樣式塊的樣式。此外,還有兩個內聯樣式,一個正常的line-height宣告和一個重要的text-decoration宣告

優先順序順序(從低到高) 作者樣式 重要性
1 A - 第一層 normal
2 B - 第二層 normal
3 C - 最後一層 normal
4 所有未分層樣式 normal
5 內聯style normal
6 動畫
7 所有未分層樣式 !important
8 C - 最後一層 !important
9 B - 第二層 !important
10 A - 第一層 !important
11 內聯style !important
12 過渡

在所有源型別中,層中包含的正常樣式優先順序最低。在我們的示例中,與第一個宣告層(A)相關的正常樣式優先順序低於第二個宣告層(B)中的正常樣式,後者又低於第三個宣告層(C)中的正常樣式。這些分層樣式優先順序低於所有正常未分層樣式,其中包括來自unlayeredStyles.cssmoreUnlayeredStyles.css<style>本身中pcolor的正常樣式。

如果A、B或C中的任何分層樣式具有與元素匹配的更高特異度的選擇器,類似於:root body p { color: black; },則無關緊要。這些宣告因而被排除在考慮之外;正常的堆疊樣式優先順序低於正常的未堆疊樣式。但是,如果在unlayeredStyles.css中找到了更具體的選擇器:root body p { color: black; },因為源和重要性具有相同的優先順序,特異度將意味著更具體的黑色宣告將獲勝。

對於宣告為!important的樣式,層的優先順序順序是顛倒的。在層中找到的重要宣告優先於在層外找到的重要宣告。在第一層(A)中找到的重要宣告優先於在B層中找到的重要宣告,後者又優先於在C層中找到的重要宣告,而C層又優先於在層外找到的重要宣告。

內聯樣式

僅與作者樣式相關的內聯樣式,使用style屬性宣告。正常內聯樣式優先於任何其他正常作者樣式,無論選擇器的特異度如何。如果line-height: 2;在任何五個匯入的樣式表中的:root body p選擇器塊中宣告,行高仍然是1.6。正常內聯樣式不優先於動畫或過渡屬性。

重要的內聯樣式優先於所有其他作者樣式,無論它們是重要的、內聯的還是分層的。重要的內聯樣式也優先於動畫屬性,但不優先於過渡屬性。有三件事可以覆蓋重要的內聯樣式

  • 重要的使用者樣式。
  • 重要的使用者代理樣式。
  • 過渡屬性。

重要性和層

對於重要樣式,源型別優先順序順序是顛倒的。在任何層疊層之外宣告的重要樣式優先順序低於作為層的一部分宣告的樣式。早期層中出現的重要樣式優先於後續層疊層中宣告的重要樣式。

例如以下CSS

css
p {
  color: red;
}

@layer B {
  :root p {
    color: blue;
  }
}

儘管紅色是首先宣告的,並且選擇器特異度較低,但由於未分層CSS優先於分層CSS,段落將是紅色。如果我們在段落上包含一個內聯樣式將其設定為不同的顏色,例如<p style="color: black">,則段落將是黑色。

當我們向這段CSS新增!important時,優先順序順序與樣式表相反

css
p {
  color: red !important;
}

@layer B {
  :root p {
    color: blue !important;
  }
}

現在段落將是藍色。最早宣告的層中的!important優先於後續層和未分層的重要宣告。如果內聯樣式包含!important,例如<p style="color: black !important">,則段落將再次是黑色。內聯重要性確實優先於所有其他作者宣告的!important宣告,無論特異度如何。

注意: !important標誌會顛倒層疊層的優先順序。因此,儘量不要使用!important來覆蓋外部樣式。相反,請使用@import以及layer關鍵字或layer()函式將外部樣式表(來自框架、小部件樣式表、庫等)匯入到層中。將樣式表作為CSS中的第一個宣告匯入到層中會降低它們的優先順序,而稍後在CSS中定義的作者定義的層將具有更高的優先順序。!important標誌應該很少使用,如果使用的話,也只是在第一個宣告層中,用於保護所需的樣式不被後來的覆蓋。

正在過渡的樣式優先於所有重要樣式,無論它們是如何宣告的。

完整的層疊順序

現在我們對源型別和層疊層優先順序有了更好的理解,我們意識到層疊順序中的表格可以更準確地表示為以下表格

優先順序順序
(從低到高)
樣式源重要性
1使用者代理 - 首先宣告的層normal
使用者代理 - 最後宣告的層
使用者代理 - 未分層樣式
2使用者 - 首先宣告的層normal
使用者 - 最後宣告的層
使用者 - 未分層樣式
3作者 - 首先宣告的層normal
作者 - 最後宣告的層
作者 - 未分層樣式
內聯style
4動畫
5作者 - 未分層樣式!important
作者 - 最後宣告的層
作者 - 首先宣告的層
內聯style
6使用者 - 未分層樣式!important
使用者 - 最後宣告的層
使用者 - 首先宣告的層
7使用者代理 - 未分層樣式!important
使用者代理 - 最後宣告的層
使用者代理 - 首先宣告的層
8過渡

哪些CSS實體參與層疊

只有CSS屬性/值對宣告參與層疊。CSS at-rule描述符不參與層疊,HTML表示性屬性不是層疊的一部分。

@ 規則

包含除宣告之外的實體的CSS at-rules,例如包含描述符@font-face規則,不參與層疊。

在大多數情況下,at-rule中定義的屬性和描述符不參與層疊。只有at-rule作為一個整體參與層疊。例如,在@font-face規則中,字型名稱由font-family描述符標識。如果定義了幾個具有相同描述符的@font-face規則,則只考慮最合適的@font-face作為一個整體。如果多個完全合適,則使用演算法的步驟1、2和4比較整個@font-face宣告(at-rule沒有特異度)。

雖然大多數at-rule中包含的宣告——例如@media@document@supports中的宣告——參與層疊,但at-rule可能會使整個選擇器不相關,正如我們在基本示例中的列印樣式所看到的那樣。

@keyframes中的宣告不參與層疊。與@font-face一樣,只有@keyframes作為一個整體透過層疊演算法進行選擇。動畫的優先順序順序在下面描述

當涉及@import時,@import本身不參與層疊,但所有匯入的樣式都參與。如果@import定義了命名層或匿名層,則匯入的樣式表的內容將放置到指定的層中。所有其他使用@import匯入的CSS都被視為最後宣告的層。這在上面已經討論過。

最後,@charset遵循特定的演算法,不受層疊演算法的影響。

表示性屬性

表示性屬性是源文件中可以影響樣式的屬性。例如,當包含時,已廢棄的align屬性設定了幾個HTML元素的對齊方式,而fill屬性定義了用於繪製SVG形狀和文字的顏色,並定義了SVG動畫的最終狀態。雖然它們是作者樣式,但表示性屬性不參與層疊。

如果使用者代理支援HTML表示性屬性,則HTML和SVG中包含的有效表示性屬性,例如alignfill屬性,會被轉換為相應的CSS規則(所有SVG表示性屬性都支援作為CSS屬性),並以特異度等於0的方式插入到作者樣式表中的任何其他樣式之前。

表示性屬性不能宣告為!important

CSS動畫和層疊

CSS動畫,使用@keyframes at-rules,定義了狀態之間的動畫。@keyframes不層疊,這意味著在任何給定時間,CSS只從一組@keyframes中獲取值,並且從不混合多個。如果定義了多個具有相同動畫名稱的@keyframes集,則使用具有最高優先順序的源和層中最後定義的一組。其他@keyframes被忽略,即使它們動畫化不同的屬性。

css
p {
  animation: infinite 5s alternate repeatedName;
}

@keyframes repeatedName {
  from {
    font-size: 1rem;
  }
  to {
    font-size: 3rem;
  }
}

@layer A {
  @keyframes repeatedName {
    from {
      background-color: yellow;
    }
    to {
      background-color: orange;
    }
  }
}

@layer B {
  @keyframes repeatedName {
    from {
      color: white;
    }
    to {
      color: black;
    }
  }
}

在此示例中,有三個名為repeatedName的獨立動畫宣告。當animation: infinite 5s alternate repeatedName應用於段落時,只應用一個動畫:基於源和層優先順序順序,未分層CSS中定義的關鍵幀動畫優先於分層關鍵幀動畫宣告。在此示例中,只有元素的字型大小會被動畫化。

注意: 沒有重要的動畫,因為@keyframes塊中包含!important作為值一部分的屬性宣告會被忽略。

重置樣式

在內容修改樣式完成後,它可能會發現自己需要將它們恢復到已知狀態。這可能發生在動畫、主題更改等情況下。CSS屬性all允許您快速將CSS中的(幾乎)所有內容恢復到已知狀態。

all允許您選擇立即將所有屬性恢復到其初始(預設)狀態、從層疊的上一級繼承的狀態、特定源(使用者代理樣式表、作者樣式表或使用者樣式表)的狀態,甚至完全清除屬性的值。

規範

規範
CSS 層疊與繼承第四級

另見