現代 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, 聲明瞭一個 級聯層,同一層內的規則將級聯在一起,這給予了開發者對層疊機制的更多控制。

除去我們非常熟悉的 @mediakeyframes 以及 @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;
    }
}

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

在用戶開啓了 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() 全局統一處理,當開啓配置時,用於實現類似這樣的功能:

什麼是色彩對比度

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

這裏就有一個概念 -- 顏色對比度,簡單地說,描述就是兩種顏色在亮度(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;
    }
}

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

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) 吧:

image

大部分瀏覽器都已經支持了,我們已經可以開始使用起來了,使用 @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 個數不定,但是均分每行的空間,並且能夠自動帶上序號,像是這樣:

使用 counter-resetcounter-incrementcounter() 這一套,非常的簡單就能實現,像是這樣:

<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-resetcounter-incrementcounter() 這一套更多的生成的數字類型的計數器。

但是,數字類型的計數器無法滿足當前全球化的排版的訴求。基於此,@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;
}

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

有了這個,我們就可以將上述的 symbols 替換成其他我們喜歡我計數圖形,譬如 emoji 圖形:

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

看看效果:

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 自定義屬性:

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

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

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

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

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

雖然我們設定了 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 設定過渡動畫,再看看這次的效果:

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

當然,如果頁面內的 @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 @layer 的作用可以清晰的被窺見。

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

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

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

@container 容器查詢

@container:提供了一種,基於容器的可用寬度來改變佈局的方式。

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

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

像是這樣:

通過瀏覽器視窗大小的變化,藉助媒體查詢,實現不一樣的佈局。

但是,在現如今,大部分 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 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 來模擬單個容器的寬度變化,在這種情況下,容器查詢能夠做到在不同寬度下,改變容器內部的佈局。

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

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

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

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

  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 實現頁面滾動與 CSS 動畫的結合,像是這樣:

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

意思是,即便目前有一些瀏覽器已經支持了 @scroll-timeline,但是它很快又將要退出歷史舞臺。不再建議再使用這個 at-rule 規則。

這裏,@scroll-timeline 雖然被廢棄了,但是 CSS 將會換一種實現方式捲土重來。

總結一下

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

  • @color-profile:允許定義並命名一個顏色配置文件
  • @font-feature-values:主要是相對字體功能的拓展

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

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

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

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章