元件專屬的淺色/深色主題

網頁功能: color-scheme light-dark()

雖然 color-scheme 屬性通常設定在根層級,但也可以設定在個別元素上,強制這些元素使用與頁面其他部分不同的色彩配置。如果元件必須一律以特定色彩配置顯示 (例如一律以深色或淺色模式顯示),這項功能就非常實用。

使用案例範例包括:

  • 基於美觀考量,即使在淺色模式網頁上,也經常以深色模式顯示的元素,例如程式碼區塊、媒體播放器、相片庫
  • 如果某個區域包含專為淺色背景設計的媒體 (例如圖片、影片、插圖、列印預覽),即使頁面其他部分採用深色模式,該區域仍可設為淺色模式。
  • 色彩配置由使用者層級設定控制的元素,例如元件預覽
  • 不支援淺色和深色模式的嵌入內容
  • 設計工具、地圖、圖表、遊戲等

何時該變更顏色,何時該強制 color-scheme?

並非所有在淺色模式下使用深色背景搭配淺色文字,或在深色模式下使用淺色背景搭配深色文字的元素,都需要不同的 color-scheme。舉例來說,在淺色模式下,主要按鈕可能會顯示為藍色,並搭配白色文字,但這不保證會使用 color-scheme: dark。

一般來說,使用不同 color-scheme 的元素通常是複雜的表面,會建立自己的視覺情境,而不是簡單的淺層容器。

考慮在元素上使用不同的 color-scheme 時,請自問:

  • Should built-in browser UI that is not otherwise customized (e.g. form controls, scrollbars, etc) use that color-scheme or adapt to the page's color-scheme? -> 如果是前者,請勿使用 color-scheme。
  • 任何 light-dark() 顏色是否應像頁面其他部分一樣解析,或根據覆寫值解析?-> 如果是前者,請勿使用 color-scheme。
  • 後代是否應位於該 color-scheme 中?如果沒有,請勿使用 color-scheme。

基本導入

元件專屬覆寫通常 (但並非絕對) 用於透過全域 color-scheme 支援多種色彩配置的頁面。如要瞭解如何妥善實作全頁深色模式,請參閱「深色模式」。

設定全頁 color-scheme 後,如果使用的顏色符記會受其影響 (例如透過 light-dark()),只要在特定元件上設定 color-scheme,即可覆寫該子樹狀結構的色彩模式:

pre, code, .dark {
  color-scheme: dark;
}

請注意,部分瀏覽器會自動將元件調整為不同的色彩配置。如要在所有情況下強制使用指定的色彩配置,請使用 only,也就是 color-scheme: only dark;,而非 color-scheme: dark;。

調整非顏色值

light-dark() 目前僅適用於顏色。

最佳做法

  • 強制:請勿在沒有背景的元素上設定 color-scheme,否則可能會混合不同色彩配置的背景和文字顏色組合,導致文字無法閱讀。
  • 選用:雖然重複使用與全頁深色模式相同的顏色組合比較簡單,但我們可以為這些元件定義不同的顏色組合。舉例來說,如果淺色模式頁面使用深色模式元件,我們可能會希望該元件的顏色比在深色模式頁面中使用的相同元件稍淺。

已知問題

重要注意事項:light-dark() 顏色的繼承

light-dark() 會在計算值時間解析。也就是說,任何設為 light-dark() 顏色的沿用 <color> 屬性,都只會將兩種顏色中的其中一種傳遞給子系,而不是 light-dark() 運算式本身。

包括:

  • 繼承的內建顏色屬性,例如 color、accent-color、fill、stroke、text-shadow、caret-color
  • 任何已註冊且可繼承的自訂屬性,包含 syntax: <color> 和 inherits: true
  • 任何其他 <color> 屬性設為 inherit

這代表您應該:

  • 請勿將用於保存設計權杖 (例如 --surface-color) 的自訂屬性註冊為 <color>。權杖必須保留 light-dark() 快速上架,後代才能在不同的 color-scheme 下重新解析。
  • 在元素上設定 color-scheme 時,請重新指定任何可能已設為 light-dark() 值的繼承 <color> 屬性 (直接或透過設計權杖),即使該屬性指向相同的設計權杖也一樣。
  • 請勿在具有 color-scheme 覆寫的元素上,對 <color> 屬性使用 inherit (可在其子系上使用)。
  • 務必在相反情況下使用已註冊的 <color> 屬性:當您刻意要擷取祖系的解析顏色,並防止其在子系的 color-scheme 下重新解析時。例如擷取頁面背景,以便在其他地方使用。
  • 如要為顏色加上動畫效果,請在要加上動畫效果的元素上,使用已註冊 @property 的獨立 <color> 屬性 (必須註冊才能進行顏色內插);這不是設計權杖,而是每個元素的動畫目標,因此不會與上述規則衝突。

範例:

:root {
    --accent-color: light-dark(blue, skyblue);
    --surface-color: light-dark(white, #222);
    --text-color: light-dark(#111, white);

    color-scheme: light dark;
    accent-color: var(--accent-color);
    color: var(--text-color);
}

body {
    /* --surface-color dynamically switches despite being inherited because --surface-color is not registered */
    background: var(--surface-color);
}

pre, code {
    color-scheme: dark;
    background: var(--surface-color);

    /* Without this, accent-color would be blue, not skyblue! */
    accent-color: var(--accent-color);

    /* Without this, text-color would be #111, not white! */
    color: var(--text-color);
}

使用 color-scheme 時應注意的問題

  • Chrome 和 Firefox 會遵循 iframe 的 color-scheme:以正確的色彩配置算繪嵌入頁面,並調整嵌入頁面的 prefers-color-scheme 媒體查詢,以反映嵌入內容的 color-scheme。Safari 則不會,即使在 iframe 內,也會將 prefers-color-scheme 解析為系統設定。
    • 如果您同時控制父項和 iframe:請在 iframe 建構時,透過網址參數 (?theme=dark) 或 postMessage() (也可讓您回應執行階段變更),明確將父項的色彩配置傳遞至 iframe。在 iframe 中,根據該信號在 <html> 上設定類別 (和/或在 :root 上設定 color-scheme),而不是依賴 prefers-color-scheme。
    • 如果您只能控制嵌入網頁:在 Safari 中,無法可靠地從 iframe 內偵測嵌入內容的 color-scheme。在嵌入 API 上公開明確的主題參數 (例如查詢字串或 postMessage 通訊協定),並為嵌入者提供說明文件。

備用策略

color-scheme 的備用方案和瀏覽器支援

color-scheme 屬性是漸進增強。不支援這項屬性的瀏覽器會忽略該屬性,並使用預設的淺色模式 UI。

如要配合使用者在舊版瀏覽器中的偏好設定,請使用 prefers-color-scheme 媒體查詢,在使用者偏好深色模式時提供不同顏色。

  • 務必使用媒體查詢,切換 :root 或 html 的自訂屬性
  • 除非元件需要顏色以外的特定深色模式自訂設定,否則請避免在個別元件上使用媒體查詢。
:root {
  /* Define brand colors for each mode */
  --color-brand-light: #0056b3;
  --color-brand-dark: #00e5ff;
  --color-brand: var(--color-brand-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --color-brand: var(--color-brand-dark);
  }

  /* Ignored in older browsers */
  color-scheme: light dark;
}

button.primary {
    background-color: var(--color-brand);
}

light-dark() 的備用方案和瀏覽器支援

如果瀏覽器支援 color-scheme,但尚未支援 light-dark(),則應先將顏色的淺色和深色版本定義為自訂屬性,並使用 prefers-color-scheme 媒體查詢設定各個模式的顏色,如以下範例所示:

:root {
  /* Define browser UI accent color for each mode */
  --brand-accent-light: #0056b3;
  --brand-accent-dark: #00e5ff;
  --accent-color: var(--brand-accent-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --accent-color: var(--brand-accent-dark);
  }

  /* OPTIONAL: use light-dark() for more control of built-in UI colors */
  @supports (color: light-dark(white, black)) {
    --accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
  }

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;

  /* Example inherited color property */
  accent-color: var(--accent-color);
}

pre, code {
  color-scheme: dark;

  /* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
  accent-color: var(--accent-color);
}