在 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 文件。

透過三項編輯檢查繼承邊界

以下檢查會將文件變成簡單的診斷練習。每次編輯後儲存原始碼並重新載入瀏覽器。請保留原始文件的副本,或在嘗試下一項之前復原變更。

  1. 只將 body 的色彩改為 #4b465f。Label A 和 Label E 周圍的文字應該會改變。圖塊會保留自己的色彩。
  2. 還原頁面色彩,然後只將 .tile 改為 color: #526637。其中一般標題和段落文字應該會跟著改變,包括 Label C。Caption D 會保留其明確色彩。
  3. 還原圖塊色彩,然後暫時在樣式表開頭加入 * { 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 上設定頁面值,並將元件色彩保留在元件根元素上。只有在設計要求例外時,才保留元素層級的宣告。

接著重做示範中的三項編輯。如果一般元件文字仍未跟隨父元素變化,請找出是否有宣告指定該後代。如果變更元件的邊框沒有改變任何後代的邊框,這是預期行為:邊框的繼承方式與文字色彩不同。繼承。

好維護的預設值都有明確的歸屬位置。將頁面字體排版放在頁面邊界,將元件呈現方式放在元件邊界,並將有意設定的例外放在需要它的元素上。只有在確實打算套用到每個匹配元素時,才使用通用宣告,並透過簡單的本機頁面確認這些邊界。

來源