實作淺色與深色配色方案

最後更新: 2023-12-05 18:27:09 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

配色方案實作需要三個層次:

  1. 訪客尚未作出網站特定選擇時使用的系統偏好。
  2. 可以覆寫系統偏好的明確網站偏好。
  3. 讓元件不依賴字面淺色與深色值的語意色彩。

宣告語意色彩 #

依角色而不是外觀命名變數:

css
:root {
  color-scheme: light dark;
  --surface: #ffffff;
  --surface-raised: #f5f6f8;
  --text: #17191c;
  --text-muted: #5d6470;
  --border: #d7dbe0;
  --accent: #1659b7;
}

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

color-scheme 告訴使用者代理程式頁面支援哪些方案,使表單控制項、捲軸與系統色彩相符。它不會定義網站的自訂調色盤。

等效的早期提示可讓瀏覽器在樣式表送達前選擇合適的畫布色彩:

html
<meta name="color-scheme" content="light dark">

根據系統偏好套用深色調色盤:

css
@media (prefers-color-scheme: dark) {
  :root {
    --surface: #17191c;
    --surface-raised: #22262b;
    --text: #eef0f2;
    --text-muted: #b5bbc3;
    --border: #3a4048;
    --accent: #8ab4f8;
  }
}

不應使用已過時的 no-preference 值。

定義明確的覆寫優先順序 #

將訪客的明確選擇儲存為 light、dark 或 system。根元素上的屬性可以覆寫媒體查詢:

css
:root[data-theme="light"] {
  color-scheme: light;
  --surface: #ffffff;
  --surface-raised: #f5f6f8;
  --text: #17191c;
  --text-muted: #5d6470;
  --border: #d7dbe0;
  --accent: #1659b7;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --surface: #17191c;
  --surface-raised: #22262b;
  --text: #eef0f2;
  --text-muted: #b5bbc3;
  --border: #3a4048;
  --accent: #8ab4f8;
}

JavaScript 狀態轉換保持簡短:

js
function setTheme(theme) {
  const root = document.documentElement;

  if (theme === "system") {
    delete root.dataset.theme;
    localStorage.removeItem("theme");
    return;
  }

  root.dataset.theme = theme;
  localStorage.setItem("theme", theme);
}

由於 local storage 可以變更,指派前要限制儲存值:

js
const saved = localStorage.getItem("theme");
if (saved === "light" || saved === "dark") {
  document.documentElement.dataset.theme = saved;
}

在文件 head 或內嵌啟動腳本中執行初始化,並在 body 呈現前完成。太晚套用可能短暫顯示錯誤調色盤。具有嚴格內容安全政策的網站可以使用 nonce,或根據已驗證 Cookie 在伺服器端產生根屬性。

回應系統變更 #

當 CSS 媒體查詢控制系統模式時,不需要監聽器。只有在應用程式狀態或畫布必須回應時,JavaScript 才需要監聽器:

js
const query = matchMedia("(prefers-color-scheme: dark)");

query.addEventListener("change", ({ matches }) => {
  if (!document.documentElement.dataset.theme) {
    redrawCanvas(matches ? "dark" : "light");
  }
});

明確的網站偏好不應被之後的作業系統變更覆寫。

為嵌入文件設定主題 #

內嵌 SVG 會參與父文件的級聯。透過 <img> 載入的 SVG 與透過 <iframe> 載入的文件具有獨立樣式樹,因此父文件自訂屬性不會跨越這些邊界。

嵌入元素使用的 color-scheme 可以影響嵌入文件的畫布與其 prefers-color-scheme 結果,但不會注入父文件撰寫的調色盤。對於應用程式控制的主題,請為同一應用程式的框架使用資源變體或刻意設計的 postMessage 協定。套用任何狀態前,先驗證訊息來源。

保留對比與意義 #

深色模式不是機械式反轉。請驗證:

  • 文字與互動控制項對比。
  • 兩種方案中的焦點指示器。
  • 不單靠不透明度表示的停用與柔和狀態。
  • 除色相外也使用標籤或形狀的圖表與狀態色彩。
  • 影像、陰影、語法醒目提示與原生表單控制項。

使用語意變數,讓對比修正可以在整個網站變更同一角色。另行測試強制色彩模式,因為使用者代理程式色彩可能取代撰寫的調色盤,並移除框線陰影等效果:

css
@media (forced-colors: active) {
  .focusable {
    border: 1px solid transparent;
  }
}

在此模式中使用系統色彩與非色彩提示。只有對色彩本身承載必要資訊的內容(例如色票),才使用 forced-color-adjust: none,因為這會使元素退出使用者的對比替換。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)