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

2022-11-03 12:00:32

大部分同學都用過 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,我們將提供壓縮度更高,尺寸更小,消耗流量更少的圖片。

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

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