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 媒體查詢

開始之前,我要再講一點理論。媒體查詢可讓作者測試及查詢使用者代理程式或顯示裝置的值或功能,不受要轉譯的文件影響。這些屬性會用於 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 版和 Safari 12.1 版 (macOS 上為 12.1 版) 和 iOS 13 版 (iOS 版) 支援的電腦版和行動版 (如有) 均支援 prefers-color-scheme。如果是其他瀏覽器,請參閱「我可以使用支援表格嗎」一文。

在要求期間瞭解使用者的偏好

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

練習時使用深色模式

最後,我們來看看支援深色模式的實際效果。就像異種戰士一樣,深色模式只能選擇一種:深色或淺色,兩者不能同時使用!為什麼我要提及這一點?因為這項事實應會對載入策略造成影響。請勿強制使用者在重要轉譯路徑中下載 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 變數即可。在使用深色模式時,以語意組織樣式會非常有幫助。舉例來說,請考慮呼叫 -⁠-⁠highlight-yellow 變數 -⁠-⁠accent-color,因為「黃色」在深色模式中不一定是黃色,反之亦然。以下是範例中使用的其他變數。

/* 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 指令,然後變更「Emulate CSS media featurerefers-color-scheme」選項。

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

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

Puppeteer 是 Node.js 程式庫,提供透過開發人員工具通訊協定控制 Chrome 或 Chromium 的高階 API。透過 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 提供。