天天看點

現代 CSS 指南 -- at-rule 規則掃盲

大部分同學都用過 CSS 的螢幕寬度媒體查詢,像是這樣:

@media screen and (min-width: 900px) {
  div {
    padding: 1rem 3rem;
  }
}      

這裡表示的是與螢幕寬度相關的樣式設定,上面的代碼表示當螢幕寬度大于 900px 時,内部的樣式代碼塊才能生效。

其實不僅僅是上面的螢幕寬度媒體查詢,在 CSS 中,存在大量的以 ​

​@​

​​ 符号開頭的規則。稱之為 ​

​@規則​

​(at-rule)。本文就将介紹一下除去媒體查詢之外,其他有意思的且在未來會越來越重要的 ​

​@規則​

​ 規則。

at-rule @規則

OK,什麼是 @規則(at-rule )呢?

一個 at-rule 是一個 ​​CSS 語句​​​,以 at 符号開頭, '​

​@​

​​' (​

​U+0040 COMMERCIAL AT​

​​), 後跟一個辨別符,并包括直到下一個分号的所有内容, '​

​;​

​​' (​

​U+003B SEMICOLON​

​), 或下一個 CSS 塊,以先到者為準。

除去我們最為熟悉的 ​

​@media​

​ 之外,CSS 還有哪些 @規則 呢?

下面是一些 @規則,由它們的标示符指定,每種規則都有不同的文法:

  • ​​@charset​​, 定義樣式表使用的字元集。
  • ​​@import​​, 告訴 CSS 引擎引入一個外部樣式表。
  • ​​@namespace​​, 告訴 CSS 引擎必須考慮 XML 命名空間。

下面是一些嵌套 @ 規則,是嵌套語句的子集,不僅可以作為樣式表裡的一個語句,也可以用在條件規則組裡:

  • ​​@media​​,如果滿足媒介查詢的條件則條件規則組裡的規則生效。
  • ​​@page​​,描述列印文檔時布局的變化。
  • ​​@font-face​​,描述将下載下傳的外部的字型。
  • ​​@keyframes​​,描述 CSS 動畫的中間步驟。
  • ​​@supports​​, 如果滿足給定條件則條件規則組裡的規則生效。
  • ​​@document​​,如果文檔樣式表滿足給定條件則條件規則組裡的規則生效。 (推延至 CSS Level 4 規範)
  • ​​@viewport​​ (已廢棄),規則讓我們可以對文檔的大小進行設定。這個特性主要被用于移動裝置,但是也可以用在支援類似“固定到邊緣”等特性的桌面浏覽器,如微軟的 Edge。
  • ​​@counter-style​​​ — 一個 ​

    ​@counter-style​

    ​ 規則定義了如何把一個計數器的值轉化為字元串表示。
  • ​​@font-feature-values​​​ (plus ​

    ​@swash​

    ​, ​

    ​@ornaments​

    ​, ​

    ​@annotation​

    ​, ​

    ​@stylistic​

    ​, ​

    ​@styleset​

    ​ and ​

    ​@character-variant​

    ​), 允許作者在​​font-variant-alternates​​ 中使用通用名稱,用于在 OpenType 中以不同方式激活功能。它允許在使用幾種字型時簡化 CSS。
  • ​​@property​​ (實驗性),是​​CSS Houdini​​ API 的一部分,它允許開發者顯式地定義他們的​​css 自定義屬性​​, 允許進行屬性類型檢查、設定預設值以及定義該自定義屬性是否可以被繼承。
  • ​​@layer​​, 聲明了一個 級聯層,同一層内的規則将級聯在一起,這給予了開發者對層疊機制的更多控制。

除去我們非常熟悉的 ​

​@media​

​​、​

​keyframes​

​​ 以及 ​

​@font-face​

​​,像是 ​

​@supports​

​​、​

​@counter-style​

​​、​

​@property​

​​、​

​@layer​

​ 等都已經或将在未來 Web 應用中扮演舉足輕重的作用。

下面,就跟随本文,一起對它們一探究竟。你也可以跳過你已經掌握的,翻到對應你還不太了解的 @ 規則下,迅速了解它們。

@charset、@import、@namespace

這三個可以放在一起講解,他們的文法比較簡單,也相對好了解。其中:

  1. ​@charset​

    ​:指定樣式表中使用的字元編碼。它必須是樣式表中的第一個元素,而前面不得有任何字元。

像是這樣:

// style.css
@charset "UTF-8";      

注意,如果有多個 @charset @規則被聲明,隻有第一個會被使用。

很多人會有疑惑,這個聲明到底有什麼用呢?

事實上,如果 CSS 檔案中有任何非 ASCII 文本,例如字型名稱,僞元素的 content 屬性值、選擇器等中的非 ASCII 字元,都需要確定 CSS 解析器知道如何轉換位元組正确轉換為字元,以便它了解 CSS 代碼。

是以如果當你發現你的僞元素 content 中插入了一些内容,但是經過打包編譯後它亂碼了,很有可能是因為你忘了聲明這個字元集。

  1. ​@import​

    ​​:用于從其他樣式表導入樣式規則。這些規則必須先于所有其他類型的規則,​

    ​@charset​

    ​ 規則除外

@import 有兩種文法:

  1. url() 内包含 style sheet 的 URI
  2. 直接寫 style sheet 的 URI 的字元串

還可以直接在後面定義媒體查詢規則,像是這樣:

@import 'custom.css';
@import url('landscape.css');
@import url('landscape.css') screen and (orientation:landscape);      

合理的使用 ​

​@import​

​ 其實也是有好處的:

  1. 可以合理的控制 CSS 檔案的大小
  2. 更好的分治與複用

很多人可能會經常看到,網絡上會有各種抵制 @import的文章,不過既然設計了 @import,總有它的有用之處,不能過于絕對。使用 ​

​@import​

​ 影響頁面性能的地方主要展現在兩個方面:

  1. 影響浏覽器的并行下載下傳
  2. 優先級問題,樣式互相覆寫
  3. 導緻頁面閃爍

這裡可以簡單解釋一下。首先我們得知道,加載頁面時,link 标簽引入的 CSS 被同時加載,而 @import 引入的 CSS 将在頁面加載完畢後被加載。

CSS 解析引擎在對一個 CSS 檔案進行解析時,如在檔案頂部遇到 ​

​@import​

​​ 規則,将被替換為該 @import 導入的 CSS 檔案中的全部樣式。而 ​

​@import​

​ 内的規則其後被加載,卻會在加載完畢後置于樣式表頂部,最終渲染時,如果存在同名同優先級樣式,會被下面的同名樣式層疊,導緻所謂的優先級沖突。

實際上,浏覽器渲染的動作一般會執行多次的。最後一次渲染,一定是基于之前加載過的所有樣式整合後渲染樹進行繪制頁面的,

而由于 ​​

​@import​

​ 内的規則的加載時機問題,會在頁面内容加載完後再加載。相當于把 CSS 放在了 body 底部,進而造成了頁面的閃爍。當網絡較差時,閃爍體驗更為明顯。

  1. ​@namespace​

    ​​ :​

    ​@namespace​

    ​ 是用來定義使用在 CSS 樣式表中的 XML 命名空間的 @規則。定義的命名空間可以把通配、元素和屬性選擇器限制在指定命名空間裡的元素。

并且,任何 @namespace 規則都必須在所有的 ​

​@charset​

​​ 和 ​

​@import​

​規則之後,并且在樣式表中,位于其他任何樣式聲明之前。

總的來說,​

​@namespace​

​ 在現如今的 CSS 生态中,屬于非常冷門的一個規則。基本上我從業這麼久,沒怎麼見過這個屬性的具體使用。

如果你對它确實感興趣,可以看看這篇詳解 -- ​​spacing-out-on-css-namespaces.​​

​@media​

​​、​

​@keyframes​

​​、​

​@font-face​

這三個 @ 規則,大家應該非常熟悉。

  • ​@media​

    ​:如果滿足媒介查詢的條件則條件規則組裡的規則生效
  • ​@keyframes​

    ​:定義 CSS 動畫的中間步驟
  • ​@font-face​

    ​:描述将下載下傳的外部的字型

​@keyframes​

​​ 和 ​

​@font-face​

​ 這兩個大家肯定非常熟悉。

但是 ​

​@media​

​ 其實内有乾坤!除了螢幕寬度媒體查詢外,其實還存在非常多不同功能的媒體查詢!

下面我會列出一些在未來,我認為會越來越被提及使用到的 ​

​@media​

​ 規則。

prefers-reduced-motion 減弱動畫效果

prefers-reduced-motion 規則查詢用于減弱動畫效果,除了預設規則,隻有一種文法取值 ​

​prefers-reduced-motion: reduce​

​,開啟了該規則後,相當于告訴使用者代理,希望他看到的頁面,可以删除或替換掉一些會讓部分視覺運動障礙者不适的動畫類型。

規範原文:Indicates that user has notified the system that they prefer an interface that removes or replaces the types of motion-based animation that trigger discomfort for those with vestibular motion disorders.

vestibular motion disorders 是一種視覺運動障礙患者,中文我隻能谷歌翻譯,翻譯出來是前庭運動障礙,我感覺不太對,谷歌了一下是一種會導緻眩暈的一類病症,譬如一個動畫一秒閃爍多次,就會導緻患者的不适。

使用方法,還是上面那段代碼:

.ele {
    animation: aniName 5s infinite linear;
}

@media (prefers-reduced-motion: reduce) {
    .ele {
        animation: none;
    }
}      

如果我們有一些類似這樣的動畫:

現代 CSS 指南 -- at-rule 規則掃盲

在使用者開啟了 ​

​prefers-reduced-motion: reduce​

​​ 時,就應該把它去掉。那麼該如何開啟這個選項呢?​​MDN -- prefers-reduced-motion​​ 給出的是:

  • 在 GTK/Gnome 中,可以通過 GNOME Tweaks (在“通用”或“外觀”菜單中,取決于具體版本) 的配置,設定 gtk-enable-animations 的值為 false
  • 可以在 GTK 3 的配置檔案中的 [Settings] 子產品下設定 gtk-enable-animations = false
  • 在 Windows 10 中:設定 > 輕松擷取 > 顯示 > 在 Windows 中顯示動畫
  • 在 Windows 7 中:控制台 > 輕松擷取 > ?是計算機更易于檢視 > 關閉不必要動畫
  • 在 MacOS 中:系統偏好 > 輔助使用 > 顯示 > 減少運動
  • 在 iOS 上:設定 > 通用 > 輔助性 > 減少運動
  • 在 Android 9+ 上:設定 > 輔助性 > 移除動畫

prefers-color-scheme 适配明暗主題

​prefers-color-scheme​

​ 還是非常好了解的,它用于比對使用者通過作業系統設定的明亮或夜間(暗)模式。它有兩個不同的取值:

  • ​prefers-color-scheme: light​

    ​: 明亮模式
  • ​prefers-color-scheme: dark​

    ​: 夜間(暗)模式

文法如下,如果我們預設的是明亮模式,隻需要适配夜間模式即可:

body {
    background: white;
    color: black;
}

@media (prefers-color-scheme: dark) {
    body {
        background: black;
        color: white;
    }
}      

當然,上述隻是 CSS 代碼示意,要做到兩套主題的切換肯定不是這麼簡單,方法也很多,本文不贅述,讀者可以自行了解各種實作主題切換,或者是明暗切換的方案。

prefers-contrast 調整内容色彩對比度

​prefers-contrast​

​ 該 CSS 媒體功能是用來檢測使用者是否要求将網頁内容以更高或者更低的對比度進行呈現。其中:

  • ​prefers-contrast: no-preference​

    ​:預設值,不作任何變化
  • ​prefers-contrast: less​

    ​:希望使用對比度更低的界面
  • ​prefers-contrast: more​

    ​:希望使用對比度更高的界面

以 ​

​prefers-contrast: less​

​ 為例子,文法如下:

body {
    background: #fff; // 文字與背景對比度為 5.74
    color: #666;
}

// 提升對比度
@media (prefers-contrast: more) {
    body {
        background: #fff; // 文字與背景對比度為 21
        color: #000;
    }
}      

上面隻是僞 CSS 代碼,具體可能需要對具體的一些元素進行處理,或者使用 ​

​filter: contrast()​

​ 全局統一處理,當開啟配置時,用于實作類似這樣的功能:

現代 CSS 指南 -- at-rule 規則掃盲

什麼是色彩對比度

是否曾關心過頁面内容的展示,使用的顔色是否恰當?色弱、色盲使用者能否正常看清内容?良好的色彩使用,在任何時候都是有益的,而且不僅僅局限于對于色弱、色盲使用者。在戶外用手機、陽光很強看不清,符合無障礙标準的高清晰度、高對比度文字就更容易閱讀。

這裡就有一個概念 -- 顔色對比度,簡單地說,描述就是兩種顔色在亮度(Brightness)上的差别。運用到我們的頁面上,大多數的情況就是背景色(background-color)與内容顔色(color)的對比差異。

最權威的網際網路無障礙規範 —— ​​WCAG AA​​規範規定,所有重要内容的色彩對比度需要達到 4.5:1 或以上(字号大于18号時達到 3:1 或以上),才算擁有較好的可讀性。

prefers-reduced-transparency 減少透明元素

​prefers-reduced-transparency​

​ 該 CSS 媒體功能是用來檢測使用者是否要求減少網頁中的透明元素:

  • ​prefers-contrast: no-preference​

    ​:預設值,不作任何變化
  • ​prefers-contrast: reduce​

    ​:希望界面元素存在盡可能少的透明元素

以 ​

​prefers-contrast: reduce​

​ 為例子,文法如下:

.ele {
    opacity: 0.5;
}

// 減少透明元素
@media (prefers-contrast: reduce) {
    .ele {
        opacity: 1;
    }
}      

我在上一次,介紹這個功能的時候,它還是一片紅色,但是短短半年,整個相容性已經有了很大的提升!

現代 CSS 指南 -- at-rule 規則掃盲

prefers-reduced-data 減少資料傳輸

對于部分網速較差的地區,或者流量很貴的情況,使用者會希望減少頁面中的流量請求,基于此有了 ​

​prefers-reduced-data​

​。

​prefers-reduced-data​

​ 該 CSS 媒體查詢功能是用于告知使用者代理,希望減少頁面的流量請求。

  • ​prefers-reduced-data: no-preference​

    ​:預設值,不作任何變化
  • ​prefers-reduced-data: reduce​

    ​:希望界面元素消耗更少的網際網路流量

以 ​

​prefers-reduced-data: reduce​

​ 為例子,文法如下:

.ele {
    background-image: url(image-1800w.jpg);
}

// 降低圖檔品質
@media (prefers-reduced-data: reduce) {
    .ele {
        background-image: url(image-600w.jpg);
    }
}      

當檢測到使用者開啟了 ​

​prefers-reduced-data: reduce​

​,我們将提供壓縮度更高,尺寸更小,消耗流量更少的圖檔。

當然,上述代碼隻是個示意,我們可以做的其實有更多。

不過,這是仍處于實驗室的功能,暫時沒有任何浏覽器支援該媒體查詢~ 😢

當然,從 Chrome 85+ 開始,可以通過開啟 ​

​#enable-experimental-web-platform-features​

​ 實驗室選項開啟該功能!

​@supports​

​ 特性檢測

好,介紹完一些後續會非常重要從 ​

​@media​

​​ 規則後,我們來看看 ​

​@supports​

​。

傳統的 CSS 特性檢測都是通過 javascript 實作的,但是如今,原生 CSS 即可實作特性檢測的功能。

CSS ​

​@supports​

​ 通過 CSS 文法來實作特性檢測,并在内部 CSS 區塊中寫入如果特性檢測通過希望實作的 CSS 語句。

文法:

@supports <supports_condition> {
    /* specific rules */
}      

舉個例子:

div {
    position: fixed;
}

@supports (position:sticky) {
    div {
        position:sticky;
    }
}      

上面的例子中,​

​position: sticky​

​ 是 position 的一個比較新的屬性,用于實作黏性布局,可以輕松實作一些以往需要 Javascript 才能實作的布局,但是不一定在一些低端機型上相容。

上面的寫法,首先定義了 div 的 ​

​position: fixed​

​​ ,緊接着下面一句 ​

​@supports (position:sticky)​

​​ 則是特性檢測括号内的内容,如果目前浏覽器支援 ​

​@supports​

​​ 文法,并且支援 ​

​position:sticky​

​​ 文法,那麼 div 的 則會被設定為 ​

​position:sticky​

​ 。

我們可以看到,​

​@supports​

​​ 文法的核心就在于這一句:​

​@supports (...) { }​

​ ,括号内是一個 CSS 表達式,如果浏覽器判斷括号内的表達式合法,那麼接下來就會去渲染括号内的 CSS 表達式。除了這種最正常的用法,還可以配合其他幾個關鍵字:

​@supports not​

​​ && ​

​@supports and​

​​ && ​

​@supports or​

​@supports not​

​ -- 非

not 操作符可以放在任何表達式的前面來産生一個新的表達式,新的表達式為原表達式的值的否定。看個例子:

.container {
  translate: 50% 10%;
  rotate: 80deg;
  scale: 1.5;
}

// 如果不支援上述的文法,則 supports 内的文法生效
@supports not (scale: 1) {
  .container {
    transform: translate(50%, 10%) rotate(80deg) scale(1.5);
  }
}      

因為添加了 not 關鍵字,是以與上面第一個例子相反,這裡如果檢測到浏覽器不支援 transform 這種分開單獨的寫法 -- ​

​scale: 1​

​​ ,則将 ​

​.container​

​​ 的 transform 屬性合在一起寫,寫成 ​

​transform: translate(50%, 10%) rotate(80deg) scale(1.5)​

​。

關于 transform 的分開寫法,如果你還不太了解,可以戳:​​解放生産力!transform 支援單獨指派改變​​

​@supports and​

​ -- 與

這個也好了解,多重判斷,類似 javascript 的 ​

​&&​

​ 運算符符。用 and 操作符連接配接兩個原始的表達式。隻有兩個原始表達式的值都為真,生成的表達式才為真,反之為假。

當然,and 可以連接配接任意多個表達式看個例子:

p {
    overflow: hidden;
    text-overflow: ellipsis;
}
@supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical) {
    p {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
    }
}      

上面同時,檢測 ​

​@supports (display:-webkit-box) and (-webkit-line-clamp:2) and (-webkit-box-orient:vertical)​

​ 了三個文法,如果同時支援,則設定三個 CSS 規則。這三個文法必須同時得到浏覽器的支援,如果表達式為真,則可以用于實作多行省略效果:

​​CodePen Demo - @supportAnd​​

​@supports or​

​ -- 或

了解了 ​

​@supports and​

​​,就很好了解 ​

​@supports or​

​​ 了,與 javascript 的 ​

​||​

​ 運算符類似,表達式中隻要有一個為真,則生成表達式表達式為真。

看例子:

@supports (background:-webkit-linear-gradient(0deg, yellow, red)) or (background:linear-gradient(90deg, yellow, red)){
    div {
        background:-webkit-linear-gradient(0deg, yellow, red);
        background:linear-gradient(90deg, yellow, red)
    }
}      

上面的例子中,隻有檢測到浏覽器支援 ​

​background:-webkit-linear-gradient(0deg, yellow, red)​

​​ 或者(or) ​

​background:linear-gradient(90deg, yellow, red)​

​ 其中一個,則給 div 元素添加漸變。

​​CodePen Demo -- @supports or​​

當然,關鍵字 ​

​not​

​​ 還可以和 ​

​and​

​​ 或者 ​

​or​

​ 混合使用。感興趣的可以嘗試一下。

Can i use?

相容性來看,先看看 ​​Can i use(更新至 2022/10/13)​​ 吧:

現代 CSS 指南 -- at-rule 規則掃盲

大部分浏覽器都已經支援了,我們已經可以開始使用起來了,使用 ​

​@supports​

​ 實作漸進增強的效果。

​@counter-style​

​ CSS 計數器

​@counter-style​

​:是一個 CSS at-rule,它讓開發者可以自定義 counter 的樣式。一個 @counter-style 規則定義了如何把一個計數器的值轉化為字元串表示。

利用 ​

​@counter-style​

​,我們可以建構自定義的計數器樣式。

當然,在 ​

​@counter-style​

​ 之前,CSS 還有一種實作簡單計數器的規範,它由如下幾個屬性共同構成:

  • ​counter-reset​

    ​: 初始化計數器的值
  • ​counter-increment​

    ​:在初始化之後,計數器的值就可以使用 counter-increment 來指定其為遞增或遞減
  • ​counter()​

    ​:計數器的值可以使用 counter() 或 counters() 函數以在 CSS 僞元素的 content 屬性中顯示

我們來看最簡單的一個例子,我們想實作一個 ul 布局,其中的 li 個數不定,但是均分每行的空間,并且能夠自動帶上序号,像是這樣:

現代 CSS 指南 -- at-rule 規則掃盲

使用 ​

​counter-reset​

​​、​

​counter-increment​

​​、​

​counter()​

​ 這一套,非常的簡單就能實作,像是這樣:

<ul>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

<ul>
  // ... <li> 個數不定
</ul>

<ul>
  // ... <li> 個數不定
</ul>      

給每個 li 元素标序号這個事情就可以交給 CSS 計數器:

ul {
  display: flex;
  justify-content: space-around;
  counter-reset: stepCount;
}
li {
  position: relative;
}
li::before {
  position: absolute;
  counter-increment: stepCount 1;
  content: counter(stepCount); 
}      

簡單解釋一下:

  1. 在 ​

    ​ul​

    ​​ 的樣式中,每次都會初始化一個 CSS 計數器 ​

    ​stepCount​

    ​,預設值為 0
  2. 在 ​

    ​li::before​

    ​​ 中的 ​

    ​counter-increment: stepCount 1​

    ​ 表示每次調用到這裡,stepCount 的值加 1
  3. 最後通過 ​

    ​counter(stepCount)​

    ​ 将目前具體的計數值通過僞元素的 content 顯現出來

OK,那麼為什麼有了上述的 CSS 計數器規範後,又新增了 ​

​@counter-style​

​ CSS 計數器規範呢?

​@counter-style​

​ 的意義

這是因為,上述的 ​

​counter-reset​

​​、​

​counter-increment​

​​、​

​counter()​

​ 這一套更多的生成的數字類型的計數器。

但是,數字類型的計數器無法滿足目前全球化的排版的訴求。基于此,​

​@counter-style​

​ 規則用一種開放的方式彌補了這一缺點,在預定義的樣式不能滿足需求時,它可以使開發者自定義他們自己的計數器樣式。

舉個例子,我們使用 MDN 上的例子作為示例:

<ul>
    <li>Lorem ipsum dolor sit amet, consectetur adipisicing elit. </li>
    <li>Lorem ipsum dolor sit amet, consectetur adipisicing elit. </li>
    <li>Lorem ipsum dolor sit amet, consectetur adipisicing elit. </li>
    <li>Lorem ipsum dolor sit amet, consectetur adipisicing elit. </li>
</ul>      
@counter-style circled-alpha {
  system: fixed;
  symbols: Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ;
  suffix: " ";
}
li {
   list-style: circled-alpha;
}      

這樣,我們就可以得到自定義的計數字首:

現代 CSS 指南 -- at-rule 規則掃盲

有了這個,我們就可以将上述的 ​

​symbols​

​ 替換成其他我們喜歡我計數圖形,譬如 emoji 圖形:

@counter-style circled-alpha {
  system: fixed;
  symbols: 😀 😄 😁 😆 😅 😂 🤣 😊 😇 🙂 🙃 😉 😌 😍 🥰 😘 😗 😙 😚 😋 😛 😝 😜 🤪 🤨;
  suffix: " ";
}
li {
   list-style: circled-alpha;
}      

看看效果:

現代 CSS 指南 -- at-rule 規則掃盲

​​CodePen Demo -- @counter-style​​

當然,實際使用過程中,​

​@counter-style​

​​ 的文法會有一點點複雜,可選的屬性也有很多,更為具體的可以仔細學習下文檔 -- ​​MDN -- @counter-style​​

​@property​

​ CSS 自定義屬性

@property CSS at-rule 是 CSS Houdini API 的一部分, 它允許開發者顯式地定義他們的 CSS 自定義屬性,允許進行屬性類型檢查、設定預設值以及定義該自定義屬性是否可以被繼承。

正常而言,我們定義和使用一個 CSS 自定義屬性的方法是這樣的:

:root {
    --whiteColor: #fff;
}

p {
    color: (--whiteColor);
}      

而有了 ​

​@property​

​ 規則之後,我們還可以像下述代碼這樣去定義個 CSS 自定義屬性:

<style>
@property --property-name {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}

p {
    color: var(--property-name);
}
</style>      

簡單解讀下:

  • ​@property --property-name​

    ​​ 中的 ​

    ​--property-name​

    ​​ 就是自定義屬性的名稱,定義後可在 CSS 中通過 ​

    ​var(--property-name)​

    ​ 進行引用
  • syntax:該自定義屬性的文法規則,也可以了解為表示定義的自定義屬性的類型
  • inherits:是否允許繼承
  • initial-value:初始值

其中,​

​@property​

​ 規則中的 syntax 和 inherits 描述符是必需的。

當然,在 JavaScript 内定義的寫法也很簡單,順便一提:

<script>
CSS.registerProperty({
  name: "--property-name",
  syntax: "<color>",
  inherits: false,
  initialValue: "#c0ffee"
});
</script>      

CSS @property 的優勢

為什麼要使用這麼麻煩的文法定義 CSS 自定義屬性呢?CSS Houdini 定義的自定義變量的優勢在哪裡?

我們來看這樣一個例子,我們有這樣一個漸變的圖案:

<div></div>      
div {
    background: linear-gradient(45deg, #fff, #000);
}      
現代 CSS 指南 -- at-rule 規則掃盲

我們改造下上述代碼,改為使用 CSS 自定義屬性:

:root {
    --colorA: #fff;
    --colorB: #000;
}
div {
    background: linear-gradient(45deg, var(--colorA), var(--colorB));
}      

得到的還是同樣的一個漸變圖:

現代 CSS 指南 -- at-rule 規則掃盲

我們再加上一個過渡效果:

:root {
    --colorA: #fff;
    --colorB: #000;
}
div {
    background: linear-gradient(45deg, var(--colorA), var(--colorB));
    transition: 1s background;
    
    &:hover {
        --colorA: yellowgreen;
        --colorB: deeppink;
    }
}      

看看滑鼠 Hover 的時候,會發生什麼:

現代 CSS 指南 -- at-rule 規則掃盲

雖然我們設定了 1s 的過渡動畫 ​

​transition: 1s background​

​,但是很可惜,CSS 是不支援背景漸變色的直接過渡變化的,我們得到的隻是兩幀之間的直接變化。

使用 CSS @property 進行改造

OK,接下來我們就使用本文的主角,使用 Houdini API 中的 CSS 自定義屬性替換原本的 CSS 自定義屬性。

簡單進行改造一下,使用 ​

​color​

​ syntax 文法類型:

@property --houdini-colorA {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}
@property --houdini-colorB {
  syntax: '<color>';
  inherits: false;
  initial-value: #000;
}
.property {
    background: linear-gradient(45deg, var(--houdini-colorA), var(--houdini-colorB));
    transition: 1s --houdini-colorA, 1s --houdini-colorB;
    
    &:hover {
        --houdini-colorA: yellowgreen;
        --houdini-colorB: deeppink;
    }
}      

我們使用了 ​

​@property​

​​ 文法,定義了兩個 CSS Houdini 自定義變量 ​

​--houdini-colorA​

​​ 和 ​

​--houdini-colorB​

​,在 hover 變化的時候,改變這兩個顔色。

需要關注的是,我們設定的過渡語句 ​

​transition: 1s --houdini-colorA, 1s --houdini-colorB​

​,在這裡,我們是針對 CSS Houdini 自定義變量設定過渡,而不是針對 ​

​background​

​ 設定過渡動畫,再看看這次的效果:

現代 CSS 指南 -- at-rule 規則掃盲

Wow,成功了,漸變色的變化從兩幀的逐幀動畫變成了補間動畫,實作了從一個漸變色過渡到另外一個漸變色的效果!而這,都得益于 CSS Houdini 自定義變量的強大能力!

​​CodePen Demo -- CSS Houdini 自定義變量實作漸變色過渡動畫​​

CSS @property 規則的強大之處在于,很多以往無法使用 CSS 進行動畫的效果,如今,借助它都可以實作!

更多 CSS @property 的用法,你可以戳 ​​CSS @property,讓不可能變可能​​

​@layer​

​@layer​

​ 可謂是 CSS 圈 2022 年最受矚目的新特性。

它的出現,目的在于讓大型項目中的 CSS 檔案及内容,可以得到更好的控制和管理。

CSS @layer 從 ​​CSS Cascading and Inheritance Level 5​​ 被規範定義。

何為 CSS @layer?簡單而言,​​CSS​​​ ​​@規則​​ 中的@layer聲明了一個 級聯層, 同一層内的規則将級聯在一起, 這給予了開發者對層疊機制的更多控制。

文法也非常簡單,看這樣一個例子:

@layer utilities {
  /* 建立一個名為 utilities 的級聯層 */
}      

這樣,我們就建立一個名為 utilities 的 @layer 級聯層。

@layer 級聯層如何使用呢?

通過 @layer 級聯層管理樣式優先級

@layer 級聯層最大的功能,就是用于控制不同樣式之間的優先級。

看下面這樣一個例子,我們定義了兩個 @layer 級聯層 A 和 B:

<div></div>      
div {
    width: 200px;
    height: 200px;
}
@layer A {
    div {
        background: blue;
    }
}
@layer B {
    div {
        background: green;
    }
}      

由于 ​

​@layer B​

​​ 的順序排在 ​

​@layer A​

​​ 之後,是以 ​

​@layer B​

​​ 内的所有樣式優先級都會比 ​

​@layer A​

​​ 高,最終 div 的顔色為 ​

​green​

​:

現代 CSS 指南 -- at-rule 規則掃盲

當然,如果頁面内的 @layer 太多,可能不太好記住所有 @layer 的順序,是以,還有這樣一種寫法。

我們可以同時命名多個 @layer 層,其後再補充其中的樣式規則。

<div></div>      
@layer B, C, A;
div {
    width: 200px;
    height: 200px;
}
@layer A {
    div {
        background: blue;
    }
}
@layer B {
    div {
        background: green;
    }
}
@layer C {
    div {
        background: orange;
    }
}      

上述代碼,我們首先定義了 ​

​@layer B, C, A​

​ 三個 @layer 級聯層。而後再後面的 CSS 代碼中補充了每個級聯層的 CSS 代碼,但是樣式的優先級為:

A > C > B

是以,最終的 div 的顔色值為 @layer A 中定義的顔色,為 ​

​blue​

​:

現代 CSS 指南 -- at-rule 規則掃盲

到這裡,CSS @layer 的作用可以清晰的被窺見。

利用 CSS @layer,我們可以将 CSS 不同子產品劃入不同的 @layer 中,利用先後順序,非常好的去控制全局的樣式優先級。

CSS @layer 的誕生,讓我們有能力更好的劃分頁面的樣式層級,更好的處理内部樣式與外部引用樣式的優先級順序,屬于比較重大的一次革新。

這裡隻是非常簡單的介紹了 @layer 規則,更詳細的,你可以戳這裡:​​2022 年最受矚目的新特性 CSS @layer 到底是個啥?​​

​@container​

​ 容器查詢

​@container​

​:提供了一種,基于容器的可用寬度來改變布局的方式。

容器查詢也是一個非常新且重要的特性,彌補了過往媒體查詢的不足。

在之前,響應式有這麼個掣肘。同一 DOM 的不同布局形态如果想要變化,需要依賴諸如媒體查詢來實作。

像是這樣:

現代 CSS 指南 -- at-rule 規則掃盲

通過浏覽器視窗大小的變化,借助媒體查詢,實作不一樣的布局。

但是,在現如今,大部分 PC 端頁面使用的是基于 Flex/Grid 的彈性布局。

很多時候,當内容數不确定的時候,即便是相同的浏覽器視窗寬度下,元素的布局及寬度可能也是不一緻的。

考慮下面這種情況:

<!-- 情況一  -->
<ul class="wrap">
    <li></li>
    <li></li>
    <li></li>
</ul>
<!-- 情況二  -->
<ul class="wrap">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>      
.wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
li {
    width: 190px;
    height: 100px;
    flex-grow: 1;
    flex-shrink: 0;
}      
現代 CSS 指南 -- at-rule 規則掃盲

這種情況下,如果需要在不同寬度下對最後一個元素做一下處理,傳統方式還是比較麻煩的。

在這種情況下,容器查詢(CSS Container Queries)就應運而生了!

容器查詢的能力

容器查詢它給予了 CSS,在不改變浏覽器視口寬度的前提下,隻是根據容器的寬度變化,對布局做成調整的能力。

還是上面的例子,簡單的代碼示意:

<div class="wrap">
    <div class="g-container">
        <div class="child">Title</div>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Necessitatibus vel eligendi, esse illum similique sint!!</p>
    </div>
</div>      
.wrap {
    width: 500px;
    resize: horizontal;
    overflow: auto;
}
.g-container {
    display: flex;
    flex-wrap: nowrap;
}
.wrap {
    /* CSS CONTAINER */
    container-name: wrap;
    container-type: inline-size;
}
@container wrap (max-width: 400px) {
    .g-container {
        flex-wrap: wrap;
        flex-direction: column;
    }
}      

像是這樣,我們通過 ​

​resize: horizontal​

​ 來模拟單個容器的寬度變化,在這種情況下,容器查詢能夠做到在不同寬度下,改變容器内部的布局。

這樣,就簡單實作了一個容器查詢功能:

現代 CSS 指南 -- at-rule 規則掃盲

注意,仔細和上面的例子作對比,這裡,浏覽器的視口寬度是沒有變化的,變化的隻是容器的寬度!

媒體查詢與容器查詢的異同,通過一張簡單的圖看看,核心的點在于容器的寬度發生變化時,視口的寬度不一定會發生變化:

現代 CSS 指南 -- at-rule 規則掃盲

我們簡單拆解下上述的代碼,非常好了解。

  1. 在 ​

    ​.warp​

    ​​ 的樣式中,通過 ​

    ​container-name: wrap​

    ​ 注冊一個容器
  2. 注冊完容器之後,便可以通過 ​

    ​@container wrap ()​

    ​ 容器查詢文法,在内部寫入不同情況下的另外一套樣式
  3. 這裡 ​

    ​@container wrap (max-width: 400px) {}​

    ​​ 的意思便是,當 ​

    ​.wrap​

    ​ 容器的寬度小于 400 px 時,采用内部定義的樣式,否則,使用外部預設的樣式

關于容器查詢更為具體的文法,我建議還是上 MDN 或者規範詳細看看 -- ​​MDN -- CSS Container Queries​​

​@scroll-timeline​

在之前,我介紹了 CSS 最新的特性 ​

​@scroll-timeline​

​,譬如這兩篇文章:

  • ​​革命性創新,動畫殺手锏 @scroll-timeline​​
  • ​​超酷炫的轉場動畫?CSS 輕松拿下!​​

​@scroll-timeline​

​ 能夠設定一個動畫的開始和結束由滾動容器内的滾動進度決定,而不是由時間決定。

意思是,我們可以定義一個動畫效果,該動畫的開始和結束可以通過容器的滾動來進行控制。

利用它,我們可以使用純 CSS 實作頁面滾動與 CSS 動畫的結合,像是這樣:

現代 CSS 指南 -- at-rule 規則掃盲

遺憾的是,這個如此好的特性,最近已經被規範廢棄,已經不再推薦使用了:

現代 CSS 指南 -- at-rule 規則掃盲

意思是,即便目前有一些浏覽器已經支援了 ​

​@scroll-timeline​

​,但是它很快又将要退出曆史舞台。不再建議再使用這個 at-rule 規則。

這裡,​

​@scroll-timeline​

​ 雖然被廢棄了,但是 CSS 将會換一種實作方式卷土重來。

總結一下

到這裡,其實還有幾個非常冷門且不太實用的 at-rule 規則,譬如:

  • ​@color-profile​

    ​:允許定義并命名一個顔色配置檔案
  • ​@font-feature-values​

    ​:主要是相對字型功能的拓展

能夠搜集到資料太少,文檔也相對簡陋,目前實用的場景太少,就不詳細展開。

綜上,可以看到,整個 at-rule 家族還是非常強大的,引入了非常多新的特性及功能,讓 CSS 生态愈發強大。讓 CSS 可以做到的事情越來越多,我們也有理由期待未來 CSS 會在 Web 領域扮演愈發重要的角色。

好了,本文到此結束,希望本文對你有所幫助 😃