ダークモード

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 にのみ適用した場合、body はウィンドウのレンダリング コンテキストを制御しないため、これらのグローバル UI サーフェスはライトモードのままになる可能性があります。

/* 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 など)とペア設定する必要があります。
  • 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() の両方の値は同じ型である必要があります。色と画像を組み合わせることはできません。 2 つのうち 1 つを画像にする必要がある場合は、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 の作成時に URL パラメータ(?theme=dark)を介して、または postMessage()(ランタイムの変更に対応することも可能)を介して、親のカラーパターンを iframe に明示的に渡します。iframe で、prefers-color-scheme に依存するのではなく、そのシグナルから <html> にクラスを設定します(または :root に 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 でスイッチを 1 回定義します。各使用サイトでは、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 も切り替えなければなりません。

代替案: 画像ごとに 1 つのカスタム プロパティ

異なる画像が数枚しかない場合は簡単ですが、すべての画像に独自のメディアクエリ ブランチが必要であり、手動切り替えでそれぞれを切り替える必要があります。

: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 のフォールバックとブラウザ サポート

この機能はプログレッシブ エンハンスメントであり、必ずしもフォールバックが必要なわけではありません。

スタイル設定が重要で、ユーザーの Baseline ターゲットが「Baseline Widely Available」以前の場合は、フォールバックとして非標準の ::-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 プロパティはプログレッシブ エンハンスメントです。このプロパティをサポートしていないブラウザでは、このプロパティは無視され、デフォルトの UI カラーが使用されます。