ダークモードの切り替え

ダークモードの切り替えにより、ユーザーは 1 つのサイトのシステム カラーパターンをオーバーライドできます。オーバーライドは <meta name="color-scheme"> を更新することで適用されます。light dark は「システム設定に従う」ことを意味し、light または dark は明示的なスキームを固定します。色(light-dark())、システムカラー、ネイティブ UI はすべて CSS の color-scheme プロパティをキーにしているため、この切り替えは、そのプロパティを <meta> と同期させることで機能します(CSS でオーバーライドを反映するをご覧ください)。

永続的な切り替え、設定コントロール、または切り替えなし?

ほとんどのサイトでは、サイトの UI(ヘッダー、ナビゲーション、フッターなど)に永続的なカラーパターンの切り替えは必要ありません。デフォルトではシステム設定に従い、手動でのオーバーライドが必要な場合は、設定ページ、パネル、オーバーレイなどの別の設定サーフェスにコントロールを配置します。

OS に対してスキームを固定することは、正当ではあるものの、まれな意図です。まれな意図は、常に表示されるコントロールではなく、段階的な開示の背後に配置する必要があります。

永続的な切り替えは、主にデベロッパー向けまたはデベロッパーに近い(デザイナー向けなど)サイトで見られます。これらのサイトでは、スキームの切り替えが一般的なウェブユーザーよりもはるかに頻繁に行われます。デベロッパー サイトでよく見られるパターンだからという理由だけで追加しないでください。このコントロールに対するデベロッパーの関心は、一般的なユーザーの関心を表すものではありません。

実装

まず、ダークモードの手順をすべて行い、両方のスキームをサポートして、デフォルトでシステム設定が使用されるようにします。

データモデル

ダークモード切り替えのデータモデルには、通常、次の 3 つの状態が含まれます。

  1. システム(保存された値なし): UI はシステムのカラーパターンに従います。可能であれば、これをデフォルトにしてください。
  2. ライト("light" として保存): システムのカラーパターンに関係なく、UI でライトモードを使用します。
  3. ダーク("dark" として保存): システムのカラーパターンに関係なく、UI でダークモードが使用されます。

この状態は通常、localStorage に保存され、ドキュメントの先頭にある <meta name="color-scheme"> 要素に反映されます。

CSS にオーバーライドを反映する

<meta> はプレゼンテーション ヒントにすぎません。作成者 CSS の color-scheme 宣言はこれをオーバーライドします。ダークモードでは :root に color-scheme: light dark が設定されるため、<meta> のみを更新しても何も起こりません。CSS が優先されます。<meta> をプロパティにマッピングして、カスケードがそれに従うようにします。

:root {
  color-scheme: light dark;

  &:has(> head > meta[name="color-scheme"][content="dark"]) {
    color-scheme: dark;
  }
  &:has(> head > meta[name="color-scheme"][content="light"]) {
    color-scheme: light;
  }
}

color-scheme: light または color-scheme: dark をルートのデフォルトとしてハードコードしないでください。基本宣言は light dark のままでなければなりません。CSS は JS に依存してはなりません。JS が実行されない場合、<meta> は light dark のままで、サイトはシステム設定に従います。何も壊れません。

永続性と FOUC の防止

localStorage でオーバーライドを保持します。システムのデフォルトに戻すときにエントリを削除します。

間違ったスキームがフラッシュされないようにするには、<meta> 要素の直後に配置されたインライン スクリプト(type=module ではなく、defer ではない)に保存された値を適用します。

<meta name="color-scheme" content="light dark">
<script>
document.querySelector('meta[name="color-scheme"]').content = localStorage.getItem("color-scheme") ?? "light dark";
</script>
  • このインライン スクリプトは、FOUC を防止する目的でのみ存在します。できるだけ小さくしてください。カラーパターンの切り替えを処理するための追加の JS は、後で読み込むことができます。
  • まれに、localStorage へのアクセスがスローされることがあります(サイトデータがブロックされている場合など)。ただし、このスクリプトは他に何も行わないため、副作用はコンソール エラーのみです。これが重要な場合は、try .. catch でラップします。テーマを永続化できない場合でも、テーマがページに適用されていることを確認します。
  • オーバーライドは別のタブから変更される可能性があります。同期を維持するには、window の storage イベントを処理します。
  • システム設定が一致するように変更されたときに、matchMedia() を使用して保存された値を削除しないでください。多くのユーザーの OS は、時刻に基づいてスキームを自動的に切り替えます。2 つが偶然一致したときに保存された値を削除すると、スキームを固定できなくなります。

HTML 値と色以外の値の分岐

色については、追加の作業は必要ありません。<meta> が color-scheme(上記)にマッピングされると、light-dark() はオーバーライドに従います。このセクションでは、他の値(font-weight、メディアソースなど)の調整について説明します。

ページがシステム設定に従うだけの場合は、<picture> または <video> ソースなど、分岐に (prefers-color-scheme: dark) を使用できます。カラーパターンをオーバーライドすると、さらに複雑になります。システム設定が信頼できる情報源ではなくなり、メディアクエリのみを受け入れる API には直接的な代替手段がありません。

分岐に .dark などの JS 適用クラスのみを使用しないでください。JS が読み込まれず、OS のデフォルトがダークモードの場合、正しく動作しません。

メディアクエリを :root:has(> head > meta[name="color-scheme"][content="dark"])(または light)セレクタと組み合わせて分岐できます。これを適切に実装する方法について詳しくは、セレクタと @ ルールの組み合わせをご覧ください。

コントロール自体のオプションも、色以外のブランチです。マークアップに両方の状態(アイコンと視覚的に非表示のアクション テキスト(「ダークモードに切り替える」など))を含め、これらのセレクタを使用して非アクティブな状態を非表示にします。display: none は、AT から冗長なオプションも削除するため、システム設定が変更されたときにコントロールを最新の状態に保つために JS は必要ありません。

HTML 内のメディアの場合、現時点で唯一の方法は、両方のバージョンを別々の要素として含め、表示を適切に切り替えることです。display: none を使用して <source> 要素を非表示にすることはできません。

ユーザー インターフェース

2 つまたは 3 つの州?

いずれの場合も、UI は、カラーパターンのシステムへの復元または設定をサポートしなければなりません。

スペースが限られているトグル(サイトのヘッダー、ナビゲーション、フッターなどの固定トグルなど)では、2 つの状態を表示することをおすすめします。スペースが限られており、設定の調整は通常、ユーザーの目標とは無関係です。

別の設定画面(ページ、パネル、オーバーレイ)のカラーパターン コントロールは、別のシナリオであり、3 つの状態(「ライト」、「ダーク」、「システム」)をすべて明示的に公開しても構いません。この場合、ユーザーは将来の動作について意図的に決定を下しており、オプションを説明する余地があります。

サイトがコンテキストアウェア スキームを実装している場合(OS がダークモードのときにライトモードを暗くするなど)も、3 つの明示的な状態が保証されます。この場合、「ライト」と「システム(現在はライト)」は実際に異なります。

2 状態の切り替え

このようなコントロールが使用される場合、現在の状態を長期的に維持するのではなく、一時的な快適さの調整(「今は明るすぎる」など)になる傾向があります。したがって、2 状態の切り替えでは、3 つの設定のうち 2 つ(システム(設定を元に戻すことができるようにするため)と、現在のリテラル値として保存されている、システムとは逆の現在の設定)を表示すれば十分です。

次の 2 つの状態のみにする必要があります。

  1. システムのデフォルト - 値は保存されず、<meta> コンテンツ light dark。現在の解決済みの値として表示されます(例: 明るい場合は太陽のアイコン)。
  2. オーバーライド - light または dark としてそのまま保存されます。

これは、現在のシステム設定に一致する明示的な状態に到達できない 3 状態コントロール(light dark、light、dark)です。

ユーザーが切り替えた場合:

  1. ターゲット スキーム = 現在レンダリングされているスキームの反対(保存された値がある場合はその値、ない場合はシステム設定)。ユーザーの意図は「システム デフォルトの逆を選択する」ではなく、「現在表示されているものとは逆のものを選択する」です。
  2. ターゲットが現在のシステム設定と異なる場合は、そのまま保存します。
  3. ターゲットが現在のシステム設定と一致する場合、ユーザーは調整を元に戻しています。保存されている値を削除します。保存しないでください。保存すると、将来のシステム変更に対してスキームが暗黙的に固定されます。
  4. <meta> のコンテンツを保存された値に設定します。値がない場合は light dark に設定します。

重要: 差異は保存時にのみ確認する必要があり、遡及的に確認することはできません。システム設定が後で一致するように変更された保存値は、保持する必要があります。多くのユーザーの OS は、時刻に基づいてスキームを自動的に切り替えます。2 つが偶然一致したときに保存された値を削除すると、スキームを固定できなくなります。

OS がライトモードに設定されている場合のシナリオ例:

  1. ユーザーが切り替える。ダークはシステム設定と異なるため、dark が保存され、サイトがダークモードになります。
  2. OS の設定がダークモードに変わります。サイトはダークモードのままです(保存された値はシステム設定と一致しますが、保持されます)。
  3. OS の設定がライトモードに戻ります。サイトは暗いままです。
  4. ユーザーが切り替える。ターゲット(ライト)がシステム設定と一致するため、保存された値が削除され、サイトは再びシステム設定に従います。

3 状態の切り替え

スペースに余裕があり、ユーザーがすでに長期的な設定を行うことを考えている場合は、3 状態コントロールを使用すると、より明確になります。この場合、データモデルは 3 つの UI オプション(システム、ライト、ダーク)に明確にマッピングされます。

認知的負荷を軽減するため、[システム] オプションでは、現在のシステム設定も表示する必要があります。たとえば、次のように表示します。

  • アイコン(システム アイコンの右下にある太陽のアイコンなど)
  • テキスト(例: 「システム(現在はライトモード)」)