ライトおよびダークカラースキームの実装

最終更新: 2023-12-05 18:27:09 +0800 +0800
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

カラースキームの実装には、次の 3 つの層が必要です。

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

ローカルストレージは変更可能なため、保存された値を代入前に制限します。

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

初期化は、body がレンダリングされる前に、文書の head またはインラインの起動スクリプトで実行してください。適用が遅れると、誤ったパレットが一瞬表示される場合があります。厳格なコンテンツセキュリティポリシーを採用するサイトでは、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 プロトコルを使用してください。状態を適用する前にメッセージのオリジンを検証してください。

コントラストと意味の維持 #

ダークモードは機械的な色の反転ではありません。次の項目を検証してください。

  • テキストとインタラクティブコントロールのコントラスト。
  • 両方のスキームにおけるフォーカスインジケーター。
  • 不透明度だけに依存しない無効状態と抑制状態。
  • 色相に加えてラベルまたは形状を使用するグラフとステータス色。
  • 画像、影、構文強調、ネイティブフォームコントロール。

セマンティック変数を使用すると、1 つの役割に対するコントラスト補正をサイト全体へ反映できます。強制カラーモードは個別にテストしてください。ユーザーエージェントの色によって定義済みパレットが置き換えられ、ボックスシャドウなどの効果が失われる場合があるためです。

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

このモードでは、システムカラーと、色以外の手掛かりを使用してください。forced-color-adjust: none は、カラースウォッチのように、具体的な色が必要な情報を伝えるコンテンツに限定してください。この指定を行うと、その要素はユーザーのコントラスト置換の対象外になるためです。

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)