過往 css 優先級中存在的問題
如果我們的頁面上存在非常多的樣式,譬如有我們開發頁面的時候的自定義樣式,也有引入的組件庫樣式。這時候樣式將會非常混亂難以管理。
當我們想覆蓋一些本身非我們書寫的樣式時候,往往不得不通過使用優先級權重更高的樣式名,去覆蓋那些樣式。
同時,當樣式優先級感到難以控制時,開發者習慣濫用 !important 去解決,這又循環導致了后續更混亂的樣式結構。
基于讓 CSS 得到更好的控制和管理的背景,CSS @layer 應運而生。
何為 CSS @layer?
CSS Cascade Layers,也叫做CSS級聯層,是Cascading and Inheritance Level5 規范中新增了一個新的 CSS 特性。
@layer聲明了一個 級聯層, 同一層內的規則將級聯在一起, 這給予了開發者對層疊機制的更多控制。語法也非常簡單,看這樣一個例子:
@layer utilities {
/* 創建一個名為 utilities 的級聯層 */
}
這樣,我們就創建一個名為 utilities 的 @layer 級聯層。
@layer語法
@layer規則可以通過三種方式其一來創建級聯層。第一種方法如上方代碼所示,它創建了一個塊級的@規則,其中包含作用于該層內部的CSS規則。
@layer utilities {
.padding-sm {
padding: .5rem;
}
.padding-lg {
padding: .8rem;
}
}
一個級聯層同樣可以通過 @import 來創建,規則存在于被引入的樣式表內:
@import(utilities.css) layer(utilities);
你也可以創建帶命名的級聯層,但不指定任何樣式。例如,單一的命名層:
@layer utilities
或者,多個命名層也可以被同時定義。例如:
@layer theme, layout, utilities
這一做法很有用,因為層最初被指定的順序決定了它是否有父級層。對于聲明而言,如果同一聲明在多個級聯層中被指定,最后一層中的將優先于其他層。
因此,在上面的例子中,如果 theme 層和 utilities 層中存在沖突的規則,那么 utilities 層中的將優先被應用。
即使 utilities 層中規則的 優先級低于 theme 層中的,該規則仍會被應用。一旦級聯層順序建立之后,優先級和出現順序都會被忽略。
這將使創建CSS選擇器變得更加簡單,因為你不需要確保每一個選擇器都有足夠高的優先級來覆蓋其他沖突的規則,你只需要確保它們出現在一個順序更靠后的級聯層中。
注:在已經聲明級聯層的名字后,它們的順序隨即被確立,你可以重復聲明某級聯層的名字來向其添加CSS規則。這些樣式將被附加到該層的末尾,且級聯層之間的順序不會改變。
其他不屬于任何一級聯層的樣式將被集中到同一匿名層,并置于所有層的前部,這意味著任何級聯層內定義的規則都將覆蓋外部聲明的規則。
嵌套層
級聯層允許嵌套,例如:
@layer framework {
@layer layout {
}
}
向 layout 層內部的 framework 層附加規則,只需用 . 連接這兩層。
@layer framework.layout {
p {
margin-block: 1rem;
}
}
匿名層
如果創建了一個級聯層但并未指定名字,例如:
@layer {
p {
margin-block: 1rem;
}
}
那么則稱為創建了一個匿名層。除創建后無法向其添加規則外,該層和其他命名層功能一致。
標準語法
@layer [ <layer-name># | <layer-name>? {
<stylesheet>
} ]
@layer如何使用
創建級聯層
級聯層可以通過多種方式聲明:
1、使用@layer 塊規則,并立即為其分配樣式:
@layer reset {
* { /* Poor Man's Reset */
margin: 0;
padding: 0;
}
}
2、使用規則@layer 語句,沒有指定任何樣式:
@layer reset;
3、將@import 與layer關鍵字或layer()函數一起使用
@import(reset.css) layer(reset);
以上每一個都創建了一個名為 的級聯層reset。
管理級聯層
級聯層會按它們聲明的順序排序。
在下面的例子中,我們建立四個級聯層:reset,base,theme,和utilities。
@layer reset { /* 創建級聯層 “reset” */
* {
margin: 0;
padding: 0;
}
}
@layer base { /* 創建級聯層 “base” */
…
}
@layer theme { /* 創建級聯層 “theme” */
…
}
@layer utilities { /* 創建級聯層 “utilities” */
…
}
按照它們的聲明順序,層順序變為:
reset
base
theme
utilities
重復使用級聯層名稱時,樣式將附加到現有級聯層。級聯層的順序保持不變,因為只有第一次的出現已經確定順序:
@layer reset { /* 創建第一個級聯層 “reset” */
…
}
@layer base { /* 創建第二個級聯層 “base” */
…
}
@layer theme { /* 創建第三個級聯層 “theme” */
…
}
@layer utilities { /* 創建第四個級聯層 “utilities” */
…
}
@layer base { /* 會將樣式添加至級聯層“base” */
…
}
重新使用級聯層名稱時層順序保持不變的使@layer 語法變得更加方便和嚴謹。使用它,可以預先建立圖層順序,然后將所有 CSS 附加到它:
@layer reset; /* 創建第一個級聯層 “reset” */
@layer base; /* 創建第二個級聯層 “base” */
@layer theme; /* 創建第三個級聯層“theme” */
@layer utilities; /* 創建第四個級聯層 “utilities” */
@layer reset { /* 添加樣式至級聯層 “reset” */
…
}
@layer theme { /* 添加樣式至級聯層 “theme” */
…
}
@layer base { /* 添加樣式至級聯層 “base” */
…
}
@layer theme { /* 添加樣式至級聯層 “theme” */
…
}
當然你可以用更短的語法來聲明級聯層,
@layer reset, base, theme, utilities;
從上面可以看出,多個級聯層被聲明時,最后一個級聯層的聲明會獲勝。像這樣,
@import(reset.css) layer(reset); /* 第一個級聯層 */
@layer base { /* 第二個級聯層 */
form input {
font-size: inherit;
}
}
@layer theme { /*第三個級聯層 */
input {
font-size: 2rem;
}
}
按以往CSS級聯來進行分析的話,form input(多層級)的優先級會大于input,但是由于級聯層所起的作用,@layer theme的input會取勝。
級聯層嵌套
級聯層支持嵌套使用,如下:
@layer base { /* 第一個級聯層*/
p { max-width: 70ch; }
}
@layer framework { /* 第二個級聯層 */
@layer base { /* 第二級聯層的嵌套子級聯層1 */
p { margin-block: 0.75em; }
}
@layer theme { /* 第二級聯層的嵌套子級聯層2 */
p { color: #222; }
}
}
在這個例子中有兩個級聯外層:
base
framework
該framework層本身也包含兩層:
base
theme
如果要將樣式附加到嵌套級聯層,需要使用以下全名來引用它,
@layer framework {
@layer default {
p { margin-block: 0.75em; }
}
@layer theme {
p { color: #222; }
}
}
@layer framework.theme {
/* 這些樣式會被添加到@layer framework層里面的theme層 */
blockquote { color: rebeccapurple; }
}
@media與@layer
@media (min-width: 30em) {
@layer layout {
.title { font-size: x-large; }
}
}
@media (prefers-color-scheme: dark) {
@layer theme {
.title { color: white; }
}
}
如果第一個@media (min-width: 30em)匹配(基于視口尺寸),則layout級聯層層將在圖層順序中排在第一位。如果只有@media (prefers-color-scheme: dark)匹配,theme則將是第一層。
如果兩者匹配,則圖層順序將為layout, theme。如果沒有匹配,則不定義層。