在 body 設定頁面文字預設值,讓繼承的屬性流入元件,並有意識地指定版面配置規則。本指南說明如何在 Mac 上建立並檢查本機 HTML 頁面,不必讓每個元素都撤銷一條通用規則。
為什麼通用規則會干擾元件預設值?
通用選擇器 * 會匹配每個元素。因此,該規則中的宣告會直接套用到每個匹配的元素;它不只是提供一個值,讓後代繼承。它也不會直接匹配偽元素;偽元素需要使用 *::before 之類的選擇器。通用選擇器。
以下 CSS 片段包含一個段落和一個巢狀文字 span 的元件:
/* Illustrative fragment: assumes a .tile with text descendants. */
* {
color: #303840;
}
.tile {
color: #245d75;
}
圖塊本身會取得藍色文字色彩。其段落和 span 則會直接取得通用規則中的深色。元件色彩無法透過一般繼承流經這些宣告。根據 CSS 預設值規則,如果在元素上沒有任何級聯值指定該屬性,繼承屬性就會取得其父元素的計算值。CSS 級聯與繼承第 4 級:預設值。
這可能讓人困惑,因為 * 的特異性為零。特異性是在適用的級聯來源和層中,比較同一元素上彼此競爭的宣告。父元素上的宣告不會與子元素上的選擇器匹配互相競爭。因此,提高父元素選擇器的特異性並不能解決這個繼承問題。特異性。
有效的做法是移除通用文字宣告,並在 body 上設定預設值。之後,後代便可繼承該值,直到某個元件或元素指定其他值為止。MDN 指出 color 是可繼承的屬性,並說明繼承會採用父元素的計算值。繼承。
哪些預設值應設在頁面、元件或每個元素上?
撰寫廣泛套用的選擇器之前,先判斷這個屬性應該沿著文字子樹流動、描述單一方塊,還是為所有方塊建立有意為之的規則。以下決策參考表將這項區分套用到簡單的自製頁面。
| 預期結果 | 建議放置位置 | 原因 |
|---|---|---|
| 一般頁面文字共用基礎色彩和字體排版 | body |
透過繼承提供文字共用的起始值 |
| 圖塊及其中的一般文字共用不同色彩 | .tile |
在元件內建立新的可繼承值 |
| 單一說明文字使用不同色彩 | .caption |
將例外套用到需要它的元素 |
| 單一面板具有內距、邊框或背景 | 面板的類別 | 讓方塊裝飾歸屬於其擁有者 |
| 方塊採用一致的尺寸計算模式 | 明確的 box-sizing 規則 |
建立明確的尺寸計算慣例 |
| 標題具有容易覆寫的外距預設值 | 指定的標題選擇器,可選擇用 :where() 包住 |
將預設值限制在預期元素上 |
繼承取決於屬性。請查閱屬性參考中的 Inherited 欄位,不要假設 body 上的每個宣告都會套用到其後代。例如,邊框預設不會繼承。繼承。
通用尺寸規則仍可能適用。使用 border-box 時,元素宣告的寬度會包含其內距和邊框;外距則不包含在該寬度內。這是特定的方塊模型選擇,並不代表所有頁面預設值都該通用套用。box-sizing。
在 Mac 上建立完整的本機示範
使用純文字模式的 TextEdit,或現有的程式碼編輯器。在 TextEdit 中,選擇 File > New,接著選擇 Format > Make Plain Text。貼上以下完整文件,選擇 File > Save,並命名為 page-defaults.html。如果系統提示,請選擇 Use .html。Apple 在其 TextEdit HTML 指南中說明了此工作流程。
此範例使用合成標籤和以 CSS 繪製的形狀。它是一份含有內嵌樣式表的完整文件,不需要準備其他檔案。我已在此檢查程式碼;尚未在 Mac 瀏覽器中執行並查看其呈現結果。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page defaults demonstration</title>
<style>
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
font-size: 1rem;
line-height: 1.5;
color: #303840;
background-color: #f4f5f6;
}
main {
max-width: 44rem;
margin: 0 auto;
padding: 2rem 1rem;
}
h1 {
line-height: 1.2;
}
.tile {
margin: 1.5rem 0;
padding: 1.25rem;
border: 2px solid currentColor;
border-radius: 0.75rem;
color: #245d75;
background-color: #ffffff;
}
.tile h2 {
margin-top: 0;
}
.shape {
width: 4rem;
height: 4rem;
background-color: currentColor;
}
.shape--round {
border-radius: 50%;
}
.caption {
color: #66523c;
}
</style>
</head>
<body>
<main>
<h1>Shape notes</h1>
<p>Page sample: <span>Label A</span>.</p>
<section class="tile">
<h2>Tile B</h2>
<div class="shape shape--round" aria-hidden="true"></div>
<p>Component sample: <span>Label C</span>.</p>
<p class="caption">Caption D has its own text color.</p>
</section>
<p>Page sample: <span>Label E</span>.</p>
</main>
</body>
</html>
在瀏覽器中開啟 page-defaults.html。若要在 TextEdit 中重新開啟原始碼,並確保 TextEdit 顯示標記內容,請選擇 TextEdit > Settings > Open and Save,並啟用 Display HTML files as HTML code instead of formatted text。在 Mac 上使用 TextEdit 處理 HTML 文件。
透過三項編輯檢查繼承邊界
以下檢查會將文件變成簡單的診斷練習。每次編輯後儲存原始碼並重新載入瀏覽器。請保留原始文件的副本,或在嘗試下一項之前復原變更。
- 只將
body的色彩改為#4b465f。Label A和Label E周圍的文字應該會改變。圖塊會保留自己的色彩。 - 還原頁面色彩,然後只將
.tile改為color: #526637。其中一般標題和段落文字應該會跟著改變,包括Label C。Caption D會保留其明確色彩。 - 還原圖塊色彩,然後暫時在樣式表開頭加入
* { color: #303840; }。包含Label C的段落現在會取得該直接宣告。之後移除這條暫時規則。
這些檢查展示了兩者的差異:元件設定可繼承的值,以及後代本身接收宣告。預期的文字行為符合規範中的繼承與預設值規則。
HTML 頁面和樣式表之間的契約是什麼?
將資源載入和選擇器行為分開處理。此範例將 CSS 嵌入 style 元素中。如果偏好使用獨立樣式表,請只將該元素的內容移到純文字檔案 page-defaults.css,並儲存在 HTML 檔案旁邊。在 head 中,用以下一行取代整個 style 元素。這是替換片段,不是另一份完整文件。
<link rel="stylesheet" href="page-defaults.css">
stylesheet 連結類型用來識別呈現文件時使用的外部 CSS 資源。href 中的檔名必須指向你實際儲存的檔案;這項宣告不會建立該檔案。HTML 標準在連結類型:stylesheet中定義了這項關係。
對此範例而言,兩者的契約很簡單:HTML 提供元素和類別,CSS 則提供其呈現方式。若在樣式表中重新命名 .tile,就必須更新 HTML 中對應的類別。若移動 CSS 檔案,就必須更新其資源參照。這兩種變更都不需要使用更強的選擇器。
如果外部樣式表版本看起來沒有樣式,請先確認 CSS 檔案以預期檔名存在,而且其內容是 CSS,而不是外圍的 HTML 文件。在修改級聯之前,先切回內嵌版本,以確認是否為資源載入錯誤。
何時該使用 :where() 或級聯層?
只有在這些功能能解決其他問題時才使用。以上完整範例不依賴其中任何一項。
使用 :where() 設定特異性為零的指定預設值
假設頁面增加了多個標題層級。以下選用的樣式表片段會設定共用間距,同時讓一般標題選擇器能輕易覆寫:
/* Optional fragment: add to the example stylesheet. */
:where(h1, h2, h3) {
margin-bottom: 0.75rem;
}
h2 {
margin-bottom: 1rem;
}
整個 :where() 選擇器(包括其引數)的特異性都是零。但將 * 替換為 :where(*) 並不能解決先前的色彩問題:它仍會匹配每個元素,並直接提供宣告。請透過繼承來解決該問題。MDN 記載 :where() 自 2021 年 1 月起已廣泛支援於各瀏覽器;若要支援較舊的瀏覽器,請查看其相容性表。:where() 偽類別。
若瀏覽器不支援 :where(),請為這些間距預設值使用一般的選擇器清單,例如 h1, h2, h3,再於相同樣式表和層中的後方放置特異性相同的例外。如此仍能保留指定的預設值,但其特異性不再是零。特異性。
當多組規則需要明確順序時使用級聯層
對於較大的樣式表,以下選用片段示範如何宣告層的順序。它與完整範例分開,不是用來取代完整 CSS。
/* Optional fragment for browsers supporting cascade layers. */
@layer defaults, components;
@layer defaults {
p {
margin-bottom: 1rem;
}
}
@layer components {
.tile p {
margin-bottom: 0.5rem;
}
}
對一般作者宣告而言,較後面的層會優先於較前面的層;未分層的一般宣告則優先於已分層的宣告。重要宣告會反轉層的順序,因此加入 !important 會改變該排列方式。層會整理互相競爭的規則;它們不會讓具有直接樣式的子元素繼承父元素的值。@layer 規則。
MDN 記載 @layer 自 2022 年 3 月起已可供各瀏覽器使用。若目標瀏覽器較舊而不支援,請將必要樣式留在層區塊之外,並採用完整範例所示的未分層方式。不要只將必要樣式放在不受支援的功能內。級聯層支援。
如何修正現有樣式表,而不增加更多覆寫?
一次處理一個屬性。若是文字色彩問題,請找出直接將色彩指定給後代的廣泛規則。從通用規則中移除該宣告,在 body 上設定頁面值,並將元件色彩保留在元件根元素上。只有在設計要求例外時,才保留元素層級的宣告。
接著重做示範中的三項編輯。如果一般元件文字仍未跟隨父元素變化,請找出是否有宣告指定該後代。如果變更元件的邊框沒有改變任何後代的邊框,這是預期行為:邊框的繼承方式與文字色彩不同。繼承。
好維護的預設值都有明確的歸屬位置。將頁面字體排版放在頁面邊界,將元件呈現方式放在元件邊界,並將有意設定的例外放在需要它的元素上。只有在確實打算套用到每個匹配元素時,才使用通用宣告,並透過簡單的本機頁面確認這些邊界。
來源
- CSS 級聯與繼承第 4 級 — W3C。
- 通用選擇器 — MDN Web Docs。
- 特異性 — MDN Web Docs。
- 繼承 — MDN Web Docs。
- box-sizing — MDN Web Docs。
- 在 Mac 上使用 TextEdit 處理 HTML 文件 — Apple 支援。
- HTML 標準:連結類型 stylesheet — WHATWG。
- :where() CSS 偽類別 — MDN Web Docs。
- @layer CSS 規則 — MDN Web Docs。