Preferencess-color-scheme:嗨,黑暗,我的老朋友

過度宣傳還是必備品?瞭解深色模式的所有相關資訊,以及如何支援這項功能,為使用者帶來更多益處!

簡介

深色模式 (前身為深色模式)

綠幕電腦螢幕
綠幕 (來源)

我們已完成深色模式的整個開發過程。在個人電腦的初期,使用深色模式並非選擇問題,而是事實問題:黑白 CRT 電腦螢幕是透過電子束在螢光螢幕上發射,而早期 CRT 使用的螢光粉是綠色的。由於文字以綠色顯示,畫面其餘部分則為黑色,因此這些模型通常被稱為綠幕

黑底白字的文書處理畫面
黑底白字 (原始)

後來推出的彩色陰轉式電視則是透過紅、綠和藍色螢光體顯示多種顏色。他們同時啟動三種螢光體,產生白光。隨著更先進的 WYSIWYG 桌面出版技術問世,讓虛擬文件看起來像實體紙張的想法也開始流行起來。

WorldWideWeb 瀏覽器中的黑底白字網頁
WorldWideWeb 瀏覽器 (Source)

這就是黑底白字設計趨勢的起源,而這項趨勢也延續到早期的以文件為主的網頁。第一款瀏覽器 WorldWideWeb (請注意,CSS 還未發明) 就是以這種方式顯示網頁。有趣的事實:有史以來第二個瀏覽器 Line Mode Browser (以終端機為基礎的瀏覽器) 在深色背景上顯示為綠色。如今,網頁和網路應用程式通常會以淺色背景搭配深色文字設計,這項基本假設也已在使用者代理程式樣式表單中硬式編碼,包括 Chrome 的樣式表單。

躺在床上使用智慧型手機
在床上使用的智慧型手機 (來源:Unsplash)

我們早已不再使用 CRT。內容消費和創作方式已轉移到使用背光 LCD 或省電 AMOLED 螢幕的行動裝置。體積更小、更便於攜帶的電腦、平板電腦和智慧型手機,帶來了新的使用模式。休閒活動 (例如瀏覽網頁、為了消遣而編寫程式,以及玩高階遊戲) 通常會在非上班時間在昏暗的環境中進行。甚至有人會在床上使用裝置。越多人在黑暗環境下使用裝置,就越多人會採用「黑底白字」的傳統設計。

使用深色模式的原因

深色模式可提升美感

當使用者被問到為什麼喜歡或想要深色模式時,最常見的回應是「比較不傷眼」,其次是「比較優雅美觀」。Apple 在深色模式開發人員說明文件中明確寫道:「對於大多數使用者而言,選擇啟用淺色或深色外觀是一種美學選擇,可能與環境光線條件無關。」

使用
System 7 CloseView (Source)

深色模式可做為無障礙工具

有些使用者確實需要使用深色模式,並將其當作另一項無障礙工具,例如視力不佳的使用者。我所能找到的最早出現的這類無障礙工具,是 System 7CloseView 功能,其中有可切換的 黑底白字白底黑字。雖然 System 7 支援彩色,但預設的使用者介面仍為黑白色。

這些以反轉為基礎的實作方式,在引入顏色後就顯現出其弱點。Szpiro 等人針對視障人士如何使用電腦裝置進行的使用者研究顯示,所有受訪使用者都不喜歡反轉圖片,但許多人偏好在深色背景中使用淺色文字。Apple 為滿足這項使用者偏好,推出了「智慧反轉」功能,可反轉螢幕上的顏色,但圖片、媒體和部分使用深色風格的應用程式除外。

電腦視覺症候群 (也稱為數位眼睛疲勞) 是一種特殊的視力障礙,定義為「眼睛和視力問題的組合,與使用電腦 (包括桌上型電腦、筆記型電腦和平板電腦) 和其他電子螢幕 (例如智慧型手機和電子閱讀器) 有關」。根據研究,青少年使用電子裝置 (尤其是在晚上) 會增加睡眠時間縮短、入睡延遲時間拉長和睡眠不足的風險。此外,根據Rosenfield的研究,藍光暴露已廣泛報告晝夜節律和睡眠週期有關,而不規律的光線環境可能會導致睡眠不足,進而影響情緒和工作表現。為了避免這些負面影響,你可以透過 iOS 的 夜間模式或 Android 的 夜燈模式 等功能調整螢幕色溫,減少藍光的影響,也可以透過深色主題或深色模式避免長時間暴露在強光或不規則的光源下。

在 AMOLED 螢幕上使用深色模式可節省電量

最後,在 AMOLED 螢幕上,深色模式可節省大量電力。以 YouTube 等熱門 Google 應用程式為主軸的 Android 個案研究顯示,節能效果可達 60%。下方影片將進一步說明這些案例研究,以及每個應用程式的省電量。

在作業系統中啟用深色模式

我剛剛介紹了為何深色模式對許多使用者來說如此重要,現在讓我們來瞭解如何支援這項功能。

Android Q 深色模式設定
Android Q 深色主題設定

支援深色模式或深色主題的作業系統通常會在設定中提供啟用這類模式的選項。在 macOS X 上,這項功能位於系統偏好設定的「General」部分,名稱為「Appearance」 (螢幕截圖);在 Windows 10 上,這項功能位於「Colors」部分,名稱為「Choose your color」 (螢幕截圖)。在 Android Q 中,您可以在「顯示」下方找到「深色主題」切換鈕 (螢幕截圖),而在 iOS 13 中,您可以在「顯示與亮度」部分變更「外觀」設定 (螢幕截圖)。

prefers-color-scheme 媒體查詢

開始之前,我要再講一點理論。Media queries 可讓作者測試及查詢 user agent 或顯示裝置的值或功能,不受要轉譯的文件影響。這些屬性會用於 CSS @media 規則,以條件方式將樣式套用至文件,以及在 HTML 和 JavaScript 等其他各種情境和語言中使用。Media Queries Level 5 引入了所謂的使用者偏好媒體功能,也就是讓網站偵測使用者偏好的內容顯示方式。

prefers-color-scheme 媒體功能可用於偵測使用者是否要求網頁使用淺色或深色主題。可搭配下列值使用:

  • light:表示使用者已通知系統,他們偏好淺色主題的頁面 (淺色背景搭配深色文字)。
  • dark:表示使用者已通知系統,表示他們偏好使用深色主題的網頁 (淺色文字搭配深色背景)。

支援深色模式

瞭解瀏覽器是否支援深色模式

由於深色模式是透過媒體查詢回報,您只要檢查媒體查詢 prefers-color-scheme 是否相符,即可輕鬆確認目前瀏覽器是否支援深色模式。請注意,我並未納入任何值,而是只檢查媒體查詢是否相符。

if (window.matchMedia('(prefers-color-scheme)').media !== 'not all') {
  console.log('🎉 Dark mode is supported');
}

截至本文撰寫時,Chrome 和 Edge 的 76 版、Firefox 的 67 版,以及 macOS 上的 Safari 12.1 版和 iOS 上的 13 版,都支援 prefers-color-scheme 在電腦和行動裝置 (適用範圍) 上運作。如果是其他瀏覽器,請參閱「我可以使用支援表格嗎」一文。

在要求時瞭解使用者的偏好設定

Sec-CH-Prefers-Color-Scheme 用戶端提示標頭可讓網站在要求時選擇取得使用者的色彩配置偏好設定,讓伺服器內嵌正確的 CSS,進而避免出現錯誤的色彩主題。

練習時使用深色模式

最後,我們來看看支援深色模式的實際效果。就像Highlander一樣,深色模式只能選擇一種:深色或淺色,兩者不能同時使用!為什麼我要提及這點?因為這項事實應會對載入策略造成影響。請勿強制使用者在重要轉譯路徑中下載 CSS,因為該路徑適用於使用者目前未使用的模式。因此,為了最佳化載入速度,我將範例應用程式的 CSS 分成三個部分,以便在實務中顯示下列最佳化建議,延後非必要的 CSS

  • style.css 包含網站上普遍使用的一般規則。
  • dark.css,只包含深色模式所需的規則。
  • light.css,只包含淺色模式所需的規則。

載入策略

後兩個 light.cssdark.css 會根據 <link media> 查詢條件載入。一開始,並非所有瀏覽器都支援 prefers-color-scheme (可使用上述模式偵測),因此我會透過在小型內嵌指令碼中透過條件插入的 <link rel="stylesheet"> 元素,動態載入預設 light.css 檔案 (淺色是任意選擇,我也可以將深色設為預設備用體驗)。為避免閃現未設定樣式的內容,我會在 light.css 載入前隱藏網頁內容。

<script>
  // If `prefers-color-scheme` is not supported, fall back to light mode.
  // In this case, light.css will be downloaded with `highest` priority.
  if (window.matchMedia('(prefers-color-scheme: dark)').media === 'not all') {
    document.documentElement.style.display = 'none';
    document.head.insertAdjacentHTML(
      'beforeend',
      '<link rel="stylesheet" href="/light.css" onload="document.documentElement.style.display = \'\'">',
    );
  }
</script>
<!--
  Conditionally either load the light or the dark stylesheet. The matching file
  will be downloaded with `highest`, the non-matching file with `lowest`
  priority. If the browser doesn't support `prefers-color-scheme`, the media
  query is unknown and the files are downloaded with `lowest` priority (but
  above I already force `highest` priority for my default light experience).
-->
<link rel="stylesheet" href="/dark.css" media="(prefers-color-scheme: dark)" />
<link
  rel="stylesheet"
  href="/light.css"
  media="(prefers-color-scheme: light)"
/>
<!-- The main stylesheet -->
<link rel="stylesheet" href="/style.css" />

樣式表架構

我盡可能使用 CSS 變數,這樣就能讓通用的 style.css 保持通用,而所有淺色或深色模式的客製化設定都會在其他兩個檔案 dark.csslight.css 中進行。您可以在下方查看實際樣式的摘錄,但這應該足以傳達整體概念。我宣告了兩個變數 -⁠-⁠color-⁠-⁠background-color,這些變數基本上會建立「深色在淺色上」和「淺色在深色上」的基準主題。

/* light.css: 👉 dark-on-light */
:root {
  --color: rgb(5, 5, 5);
  --background-color: rgb(250, 250, 250);
}
/* dark.css: 👉 light-on-dark */
:root {
  --color: rgb(250, 250, 250);
  --background-color: rgb(5, 5, 5);
}

接著,我在 style.css 中使用 body { … } 規則中的這些變數。由於這些變數是在 :root CSS 擬似類別上定義,而該選取器在 HTML 中代表 <html> 元素,且與選取器 html 相同,只是其特異性較高,因此會依序向下展開,這對我宣告全域 CSS 變數很有幫助。

/* style.css */
:root {
  color-scheme: light dark;
}

body {
  color: var(--color);
  background-color: var(--background-color);
}

在上述程式碼範例中,您可能會注意到屬性 color-scheme 包含以空格分隔的值 light dark

這會告訴瀏覽器應用程式支援哪些色彩主題,並讓瀏覽器啟用使用者代理程式樣式表的特殊變化版本,這類變化版本可用於讓瀏覽器以深色背景和淺色文字顯示表單欄位、調整捲軸列,或啟用主題感知醒目顏色。color-scheme 的確切詳細資料已在 CSS 色彩調整模組第 1 級中指定。

其他所有內容都只需要為網站上的重要內容定義 CSS 變數。在使用深色模式時,以語意組織樣式會非常有幫助。舉例來說,請考慮呼叫變數 -⁠-⁠accent-color,而非 -⁠-⁠highlight-yellow,因為「黃色」在深色模式下可能並非黃色,反之亦然。以下是範例中使用的其他變數。

/* dark.css */
:root {
  --color: rgb(250, 250, 250);
  --background-color: rgb(5, 5, 5);
  --link-color: rgb(0, 188, 212);
  --main-headline-color: rgb(233, 30, 99);
  --accent-background-color: rgb(0, 188, 212);
  --accent-color: rgb(5, 5, 5);
}
/* light.css */
:root {
  --color: rgb(5, 5, 5);
  --background-color: rgb(250, 250, 250);
  --link-color: rgb(0, 0, 238);
  --main-headline-color: rgb(0, 0, 192);
  --accent-background-color: rgb(0, 0, 238);
  --accent-color: rgb(250, 250, 250);
}

完整範例

在下方的 Glitch 嵌入內容中,您可以看到完整的範例,將上述概念付諸實踐。請嘗試在特定作業系統的設定中切換深色模式,看看頁面會如何回應。

載入影響

您可以透過這個範例,瞭解為何我會透過媒體查詢載入 dark.csslight.css。請嘗試切換深色模式並重新載入頁面:系統仍會載入目前不相符的特定樣式表單,但會將其優先順序設為最低,這樣就不會與網站目前所需的資源競爭。

網路載入圖表,顯示在淺色模式中,深色模式 CSS 的載入優先順序為最低
在淺色模式下,網站會以最低優先順序載入深色模式 CSS。
網路載入圖表,顯示深色模式中,以最低優先順序載入淺色模式 CSS
在深色模式下,網站會以最低優先順序載入淺色模式 CSS。
網路載入圖表,顯示在預設的淺色模式中,深色模式 CSS 的載入優先順序最低
在未支援 prefers-color-scheme 的瀏覽器上,以預設的淺色模式顯示網站時,系統會以最低優先順序載入深色模式 CSS。

回應深色模式變更

如同其他媒體查詢變更,您可以透過 JavaScript 訂閱深色模式變更。例如,您可以使用這個方法動態變更網頁的網頁圖示,或變更用於決定 Chrome 網址列顏色的 <meta name="theme-color">。上方的完整範例顯示實際運作情形,如要查看主題顏色和網站小圖示的變更,請在另一個分頁中開啟示範例

const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', (e) => {
  const darkModeOn = e.matches;
  console.log(`Dark mode is ${darkModeOn ? '🌒 on' : '☀️ off'}.`);
});

自 Chromium 93 和 Safari 15 起,您可以使用 meta 主題色彩元素的 media 屬性,根據媒體查詢調整顏色。系統會挑選第一個相符的項目。舉例來說,您可以為淺色模式和深色模式分別設定不同的顏色。在撰寫本文時,您無法在資訊清單中定義這些項目。請參閱 w3c/manifest#975 GitHub 問題

<meta
  name="theme-color"
  media="(prefers-color-scheme: light)"
  content="white"
/>
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />

偵錯及測試深色模式

在開發人員工具中模擬 prefers-color-scheme

切換整個作業系統的配色方案可能會很快變得惱人,因此 Chrome 開發人員工具現在可讓您以只影響目前顯示的分頁的方式,模擬使用者偏好的配色方案。開啟指令選單,開始輸入 Rendering,執行 Show Rendering 指令,然後變更「模擬 CSS 媒體功能 prefers-color-scheme」選項。

Chrome 開發人員工具「算繪」分頁中的「模擬 CSS 媒體功能 prefers-color-scheme」選項螢幕截圖

使用 Puppeteer 擷取 prefers-color-scheme 的螢幕截圖

Puppeteer 是 Node.js 程式庫,可提供高階 API,透過 DevTools 通訊協定控制 Chrome 或 Chromium。dark-mode-screenshot 提供 Puppeteer 指令碼,可讓您在深色和淺色模式下建立網頁的螢幕截圖。您可以將這個指令碼當成一次性指令碼執行,也可以將其納入持續整合 (CI) 測試套件。

npx dark-mode-screenshot --url https://googlechromelabs.github.io/dark-mode-toggle/demo/ --output screenshot --fullPage --pause 750

深色模式最佳做法

避免使用純白

您可能注意到,我沒有使用純白色。相反地,為避免在周圍深色內容上出現發光和溢色情形,我選擇了稍微偏深的白色。rgb(250, 250, 250) 之類的名稱就很適合。

重新著色及調暗相片圖像

比較下方的兩張螢幕截圖,您會發現核心主題不僅從「淺色底深色字」變成「深色底淺色字」,主圖的樣貌也略有不同。根據使用者研究顯示,在啟用深色模式時,大多數受訪者偏好稍微不那麼鮮豔亮麗的圖片。我稱之為「重新著色」

主圖片在深色模式下稍微變暗。
主圖片在深色模式下稍微變暗。
淺色模式的一般主頁橫幅。
一般主頁橫幅 (淺色模式)。

我可以透過圖片上的 CSS 濾鏡重新上色。我使用 CSS 選取器,比對所有網址中沒有 .svg 的圖片,目的是讓向量圖形 (圖示) 與圖片 (相片) 採用不同的重新著色處理方式,詳情請參閱下一節。請注意,我再次使用 CSS 變數,以便日後靈活變更篩選器。

由於只有在深色模式下才需要重新著色,也就是 dark.css 處於啟用狀態時,light.css 中才會有對應的規則。

/* dark.css */
--image-filter: grayscale(50%);

img:not([src*='.svg']) {
  filter: var(--image-filter);
}

使用 JavaScript 自訂深色模式重新著色強度

每個人的情況都不盡相同,對深色模式的需求也不同。只要採用上述重新著色方法,就能輕鬆將灰階強度設為使用者偏好設定,並透過 JavaScript 變更。此外,只要設定 0% 值,就能完全停用重新著色功能。請注意,document.documentElement 會提供文件根元素的參照,也就是我可以透過 :root CSS 擬類別 參照的相同元素。

const filter = 'grayscale(70%)';
document.documentElement.style.setProperty('--image-filter', value);

反轉向量圖形和圖示

針對向量圖形 (在我的例子中,我將其用作透過 <img> 元素參照的圖示),我會使用不同的重新著色方法。雖然研究顯示使用者不喜歡將相片反轉,但這項功能對大多數圖示都很實用。我再次使用 CSS 變數,判斷在一般和 :hover 狀態下應反轉的量。

在深色模式下,圖示會反轉。
圖示會在深色模式下反轉。
淺色模式下的一般圖示。
淺色模式中的一般圖示。

請注意,我再次只在 dark.css 中反轉圖示,而非在 light.css 中反轉,以及 :hover 如何在兩種情況下取得不同的反轉強度,以便根據使用者選取的模式,讓圖示顯示為略深或略淺。

/* dark.css */
--icon-filter: invert(100%);
--icon-filter_hover: invert(40%);

img[src*='.svg'] {
  filter: var(--icon-filter);
}
/* light.css */
--icon-filter_hover: invert(60%);
/* style.css */
img[src*='.svg']:hover {
  filter: var(--icon-filter_hover);
}

使用 currentColor 內嵌 SVG

對於內嵌 SVG 圖片,您可以利用 currentColor CSS 關鍵字,代表元素 color 屬性的值,而非使用反轉篩選器。這樣一來,您就能在預設情況下不會接收 color 值的屬性上使用 color 值。方便起見,如果 currentColor 用於 SVG fillstroke 屬性的值,則會改為從顏色屬性的繼承值取得值。更棒的是,這項功能也適用於 <svg><use href="…"></svg>,因此您可以使用不同的資源,currentColor 仍會套用在情境中。請注意,這項功能僅適用於內嵌<use href="…"> SVG,但不適用於以圖片 src 或透過 CSS 以某種方式參照的 SVG。您可以在下方的示範中看到這項功能的應用方式。

<!-- Some inline SVG -->
<svg xmlns="http://www.w3.org/2000/svg"
    stroke="currentColor"
>
  […]
</svg>

模式之間的轉換流暢

colorbackground-color 都是可動畫化的 CSS 屬性,因此從深色模式切換至淺色模式,或反之,都能順利完成。建立動畫的步驟非常簡單,只要為這兩個屬性宣告兩個 transition 即可。以下範例說明整體概念,您可以在示範中親自體驗。

body {
  --duration: 0.5s;
  --timing: ease;

  color: var(--color);
  background-color: var(--background-color);

  transition: color var(--duration) var(--timing), background-color var(
        --duration
      ) var(--timing);
}

深色模式的藝術指導

雖然一般來說,為了載入效能,我建議您只在 <link> 元素的 media 屬性中使用 prefers-color-scheme (而非在樣式表格中內嵌),但在某些情況下,您可能會想在 HTML 程式碼中直接內嵌 prefers-color-scheme。藝術指導就是這類情況。在網頁上,藝術指導會處理網頁的整體視覺外觀,以及網頁如何以視覺方式傳達、激發情緒、對比功能,以及吸引目標對象的心理。

在深色模式下,設計師可視情況決定在特定模式下最適合的圖片,以及圖片的重新著色是否不夠理想。如果與 <picture> 元素搭配使用,顯示圖片的 <source> 可依賴 media 屬性。在下方範例中,我會顯示深色模式的西半球,以及淺色模式的東半球,或是在未提供偏好設定時,所有其他情況都會預設為東半球。這當然只是為了說明。在裝置上切換深色模式,看看差異。

<picture>
  <source srcset="western.webp" media="(prefers-color-scheme: dark)" />
  <source srcset="eastern.webp" media="(prefers-color-scheme: light)" />
  <img src="eastern.webp" />
</picture>

深色模式,但新增選擇退出功能

如上文「為何採用深色模式」一節所述,深色模式是大多數使用者偏好的美觀選擇。因此,有些使用者可能會將作業系統 UI 設為深色,但仍希望以慣用方式查看網頁。建議的做法是,一開始遵循瀏覽器透過 prefers-color-scheme 傳送的信號,但可視需要允許使用者覆寫系統層級設定。

<dark-mode-toggle> 自訂元素

您當然可以自行建立這類程式碼,但也可以使用我為此目的而建立的現成自訂元素 (網頁元件)。這個類別稱為 <dark-mode-toggle>,可在頁面中加入切換鈕 (深色模式:開啟/關閉) 或主題切換器 (主題:淺色/深色),讓您全面自訂。以下示範會顯示元素的運作情形 (順帶一提,我還在所有其他 範例 悄悄加入了這個元素)。

<dark-mode-toggle
  legend="Theme Switcher"
  appearance="switch"
  dark="Dark"
  light="Light"
  remember="Remember this"
></dark-mode-toggle>
淺色模式中的深色模式切換鈕。
<dark-mode-toggle> 在淺色模式中。
淺色模式中的深色模式切換鈕。
<dark-mode-toggle> 在深色模式下顯示。

請嘗試按一下或輕觸下方示範畫面右上角的深色模式控制項。如果勾選第三和第四個控制項的核取方塊,即使重新載入頁面,系統也會記住您選取的模式。這樣一來,訪客就能在作業系統維持深色模式的情況下,以淺色模式瀏覽您的網站,反之亦然。

結論

使用及支援深色模式很有趣,而且能開啟新的設計途徑。對部分訪客而言,這可能會是無法順利使用網站,和成為滿意使用者的差別。這其中存在一些陷阱,因此務必謹慎測試,但無論如何,推出深色模式絕對是向所有使用者展現您重視他們的絕佳機會。本篇文章提到的最佳做法,以及 <dark-mode-toggle> 自訂元素等輔助工具,應該能讓您有信心打造出色的深色模式體驗。在 Twitter 上告訴我你建立了什麼內容,以及這篇文章是否實用,或是提供改善建議。感謝閱讀!🌒?

prefers-color-scheme 媒體查詢的資源:

color-scheme 中繼標記和 CSS 屬性的資源:

一般深色模式連結:

這篇文章的背景研究文章:

特別銘謝

prefers-color-scheme 媒體功能、color-scheme CSS 屬性和相關的 Meta 標記,是 👏? Rune Lillesveen 實作的工作。Rune 也是 CSS 色彩調整模組第 1 級規格書的共同編輯者。在此,我要感謝 Lukasz ZbylutRowan MerewoodChirag DesaiRob Dodson,感謝他們對本文進行詳細審查。載入策略Jake Archibald 的構想。Emilio Cobos Álvarez 已指出正確的 prefers-color-scheme 偵測方法。提供 SVG 參照和 currentColor 的提示來自 Timothy Hatcher。最後,我要感謝許多匿名參與者參與各種使用者研究,協助我們制定本文中的建議。主頁橫幅圖片由 Nathan Anderson 提供。