深色模式

color-scheme 屬性會指出網頁支援的色彩配置 (例如淺色或深色)。這會通知瀏覽器,可以自動設定原生 UI 元素 (例如捲軸、表單控制項和預設畫布背景) 的主題,以符合網站設計,並盡可能減少初始載入期間的白色閃爍。

導入作業

1. 在 HTML 中宣告支援的配置

強制:為避免「未套用主題的內容閃爍」(FOUC),請在 <head> 中加入 <meta> 標記,確保瀏覽器在開始算繪前,就知道你支援哪些主題。雖然這個 <meta> 標記可提早設定初始畫布顏色,有助於避免 FOUC,但可能無法完全消除所有瀏覽器或載入條件下的閃爍情形。

<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">

2. 將全頁色彩配置套用至 CSS :root 或 HTML

必要:將 color-scheme 屬性套用至 html 元素或 :root 虛擬類別。瀏覽器會特別查看根元素,判斷整個可視區域的主題,包括根捲軸和初始「畫布」背景。如果只套用至 body,這些全域 UI 介面可能會維持在淺色模式,因為 body 無法控制視窗的轉譯內容。

/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;
}

3. 定義淺色和深色權杖

您可以使用 light-dark() 函式定義顏色符記,自動配合不同的 color-scheme 值調整。

建議您也將原始顏色值保留在不同的自訂屬性中,這樣就能更輕鬆地以不同方式組合這些值 (並在需要時簡化備用行為)。

如要進一步控管 accent-color 或 scrollbar-color 等內建 UI 的顏色,作者可以選擇使用自訂屬性和/或 light-dark() 函式,新增自己的動態顏色。這個函式會根據元素計算出的 color-scheme 自動選擇正確的顏色,不需要多餘的媒體查詢,但基本實作不需要這個函式。

:root {
  --color-brand-light: oklch(45% 0.23 270);
  --color-brand-dark: oklch(85% 0.15 210);
  --color-brand-text-light: white;
  --color-brand-text-dark: oklch(40% 0.23 270);

  --color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
  --color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));

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

button.primary {
  /* These automatically adapt to color scheme */
  background-color: var(--color-brand);
  color: var(--color-brand-text);
}

選用:有多種系統顏色可供使用,也會自動配合使用的色彩配置 (和其他色彩模式,例如強制顏色),例如 canvas、canvastext、accentcolor (檢查支援)、buttonborder 等。這些顏色通常太有限,除了需要完全符合特定預設瀏覽器 UI 或做為備用/預設值的特定情況外,其他情況都不適用。

選用:根據情境調整顏色組合

即使覆寫系統預設值,使用 prefers-color-scheme 媒體查詢定義不同顏色組合仍有其用處,因為這會考量網頁周圍的瀏覽器和 OS Chrome 顏色 (或網頁做為 iframe 時周圍網頁的顏色)。

舉例來說,當系統設定為 dark 時,請使用稍微偏暗的淺色主題;當系統設定為 light 時,請使用對比度較高的深色主題,這樣網頁就不會被周圍的 UI 遮蓋。

精細自訂瀏覽器 UI

設定 color-scheme 後,瀏覽器 UI 就會根據使用的色彩配置調整,但這會採用 OS 預設值和/或系統顏色,可能與網站設計不完全一致。新式瀏覽器會為這些項目提供多個精細的自訂掛鉤。請勿為了自訂外觀而重新實作原生控制項,除非您已用盡新式瀏覽器提供的自訂掛鉤。

設定強調色

部分瀏覽器 UI (例如勾選的核取方塊或滑桿) 會使用強調色。根據預設,這會解析為 OS 設定,但您可以使用 accent-color 屬性,將其設為更符合網頁的顏色,例如網頁的品牌顏色。

html {
  accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}

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

瀏覽器轉譯 accent-color 的方式不盡相同。 對於通常繪製在強調色背景上的指標 (例如核取方塊標記和圓形按鈕點),瀏覽器可能會採用不同的處理方式:

  • Chromium 和 Firefox 通常會選取對比鮮明的指標顏色。
  • Safari 則可能會在淺色模式下保留白色指標,或在深色模式下保留黑色指標,同時修改指標後方的強調色。這項調整不一定能為所有提供的顏色提供足夠的對比度,因此請在 Safari 中測試重要組合,包括淺色和深色色彩配置。請勿假設 Safari 會自動選擇對比度最佳的指標顏色。

捲軸顏色

您可以搭配使用 scrollbar-color 和 light-dark(),設定符合所用色彩配置的自訂捲軸顏色。

:root {
  --color-scrollbar-track: light-dark(#eee, #222);
  --color-scrollbar-thumb: light-dark(#999, #666);
  scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}

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

  • 請勿為 scrollbar-color 加上動畫或轉場效果。WebKit 錯誤會導致捲軸在每次 scrollbar-color 變更時閃爍。
  • 在 macOS 上,系統預設會忽略 scrollbar-color (標準) 和 ::-webkit-scrollbar (舊版) 屬性,因為 macOS 使用原生「疊加」捲軸。您必須將自訂顏色與 scrollbar-width (例如 thin 或 auto) 搭配使用,強制 macOS 轉譯這些顏色。
  • 即使套用 scrollbar-width,macOS 疊加捲軸預設仍會將軌道 (邊線) 算繪為透明。如果設計要求在 MacOS 上顯示可見的軌道背景顏色,您必須將 scrollbar-gutter: stable; 套用至可捲動容器,但請注意,只有在使用者將滑鼠游標懸停在捲軸上時,才會顯示。
  • 即使使用 scrollbar-gutter: stable,在 macOS 上,軌道仍可能呈現透明。滑桿不應依賴軌道顏色來顯示。

進一步自訂

大多數瀏覽器 UI 都會公開虛擬元素,以便完整自訂外觀,例如:

  • ::placeholder
  • ::spelling-error
  • ::grammar-error
  • ::selection
  • ::search-text
  • ::target-text
  • ::file-selector-button

您可以在上述任何項目上使用 light-dark() 顏色,套用會根據所用色彩配置調整的顏色。

4. 淺色和深色模式的圖片不同

如果是 CSS 圖片,light-dark() 也可以用來提供不同的圖片值。

重要事項:light-dark() 中的兩個值必須屬於相同類型。無法同時使用顏色和圖片。 如果其中一個必須是圖片,請使用 image(<color>) 包裝顏色值。

以 JS 為基礎的主題偵測

大多數色彩配置分支應在 CSS 中完成,CSS 會自動配合變更調整。

任何讀取 matchMedia("(prefers-color-scheme: dark)").matches 的 JS 也必須處理其 change 事件,因為系統偏好設定隨時可能變更。

選用:導入色彩配置切換鈕

如要允許使用者手動覆寫這個網站的系統預設值,可以實作深色模式切換鈕。大多數網站不需要在網站 UI 中提供永久切換鈕。 通常只要預設為系統色彩配置即可,也可以在獨立的設定介面中進行設定。如要瞭解實作深色模式切換鈕 (永久或非永久) 的最佳做法,請參閱「深色模式切換鈕」。

選用:元件專屬覆寫

您可以為特定元素設定 color-scheme,覆寫全域主題。

如需更多資訊和注意事項,請參閱「元件專屬淺色/深色主題」。

已知問題

使用 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);
}

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

如果瀏覽器不支援圖片的 light-dark(),請使用 image-set() 做為切換:為每個選項提供自訂屬性的 type(),並將未啟用的選項設為不支援的 MIME 類型,讓瀏覽器捨棄該選項。啟用選項會取得空值,因此是沒有 type() 的一般 image-set() 選項。

在 :root 上定義一次切換鈕。這樣一來,每個使用網站就只需要內嵌這兩張圖片,例如 light-dark()。

:root {
  --light-type: ;
  --dark-type: type("image/do-not-use");

  /* MANDATORY: Fallback for browsers without light-dark() image support */
  @media (prefers-color-scheme: dark) {
    --light-type: type("image/do-not-use");
    --dark-type: ;
  }
}

.hero {
  background-image: image-set(url(hero-light.png) var(--light-type), url(hero-dark.png) var(--dark-type));

  @supports (background-image: light-dark(url("a"), url("b"))) {
    background-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}
  • 系統只會下載所選圖片。
  • 解決方案仍有效:image-set(url(a-1x.png) 1x var(--light-type), url(a-2x.png) 2x var(--light-type), url(b-1x.png) 1x var(--dark-type), url(b-2x.png) 2x var(--dark-type))。
  • 請勿使用 type("") 做為啟用選項。瀏覽器會將空字串視為不支援,並捨棄所有選項。
  • 與 light-dark() 不同,切換鈕不會遵循在子樹上設定的 color-scheme。手動切換鈕或任何設定 color-scheme 的子樹狀結構,也必須翻轉 --light-type 和 --dark-type。

替代方案:每張圖片一個自訂屬性

如果只有少數圖片不同,這種做法比較簡單,但每張圖片都需要自己的媒體查詢分支,而且必須手動切換每個分支。

:root {
  --hero-image: url(hero-light.png);

  @media (prefers-color-scheme: dark) {
    --hero-image: url(hero-dark.png);
  }

  @supports (background-image: light-dark(url("a"), url("b"))) {
    --hero-image: light-dark(url(hero-light.png), url(hero-dark.png));
  }
}

.hero {
  background-image: var(--hero-image);
}

image() 的備用方案和瀏覽器支援

使用單一顏色停駐點且不含位置的漸層,例如 linear-gradient(blue)。

如要支援更多瀏覽器,可以明確提供單一顏色停止點的 0 100% 位置,例如 linear-gradient(blue 0 100%)。

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

這項功能是漸進增強功能,不一定需要備用廣告。

如果樣式很重要,且使用者的基準目標是「基準廣泛可用」或更早版本,則應加入非標準 ::-webkit-scrollbar 虛擬元素做為備用。

在 @supports not (scrollbar-color: auto) 區塊中納入舊版備用屬性,避免在原生支援標準屬性和舊版 WebKit 選取器的瀏覽器中,發生兩者衝突的情況。

如果您使用自訂屬性定義顏色,這些屬性會自動套用至舊版 WebKit 選取器。您「不」需要複製這些項目。

/* Legacy fallback for WebKit/Blink browsers */
@supports not (scrollbar-color: auto) {
  .scroller::-webkit-scrollbar {
    /* Must define base size in WebKit for custom colors to be visual */
    width: 12px;
    height: 12px;
  }

  .scroller::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
  }

  .scroller::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
  }
}

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

accent-color 屬性是漸進增強。不支援這項屬性的瀏覽器會忽略該屬性,並使用預設的使用者介面顏色。