Soranes

ウェブサイトのダークモード対応のベストプラクティス

にリリースされた macOS Mojave および にリリースされた iOS 13 から OS のユーザーインターフェースの配色を暗色にするダークモードがサポートされた。 同じ時期に主要ウェブブラウザでも CSS の color-scheme プロパティや prefers-color-scheme メディア特性がサポートされたことでダークモードに対応したウェブサイトが増えてきた。 ダークモードは目への負担が大幅に軽減されるので、以前から黒画面に慣れ親しんできた IT 技術者を中心に愛用している人も多いと思う。

ウェブサイトのダークモードの対応方法についての記事はインターネット上にすでにいくつかあるが、ベストな方法ではなかったり最新の記法ではなかったりがほとんどだったので、現代のベストプラクティスを紹介する。

この記事で紹介する方法はウェブ標準技術のみを使い、特定のフレームワークやライブラリを使った方法は考慮しない。

配色設定の宣言

以下のように CSS で :root 擬似クラスに color-scheme プロパティを設定するだけでダークモード対応が完了し、ユーザーの OS やウェブブラウザの配色設定に応じてユーザーエージェントデフォルトスタイルのライト配色またはダーク配色が適用されるようになる。 この配色には文字色や背景色だけではなくスクロールバーの色やフォームコントロールの色、および CSS のシステムカラーの規定色が含まれる。


            :root {
              color-scheme: light dark;
            }
          

color-scheme プロパティは CSS で設定するのが基本だが、同プロパティは以下のとおり HTML の <meta> 要素でも設定できることが WHATWG の HTML Living Standard 仕様で定められている。


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

To aid user agents in rendering the page background with the desired color scheme immediately (rather than waiting for all CSS in the page to load), a color-scheme value can be provided in a <meta> element.

ページの背景を希望するカラースキームで即座にレンダリングできるようにするため (ページ内のすべての CSS の読み込みを待つのではなく) 、 <meta> 要素に color-scheme の値を指定することができる。

4.2.5.1 Standard metadata names in HTML Living Standard

基本的には CSS でも HTML の <meta> 要素でもどちらでも好きなほうで構わないが、ウェブページ全体に配色設定を宣言する場合は私は以下の理由から <meta> 要素のほうが筋が良いと考えている。

  • <meta> 要素はウェブブラウザのレンダリングエンジンが DOM のパースを完了した時点で配色設定が適用されるが、 CSS だと DOM のパース完了後に CSS が取得・パースされるので一瞬の画面のチラつき (白画面の後に黒画面) が発生する可能性がある。 (HTML Living Standard 仕様内でも言及されているとおり。)
  • 後述する配色設定をウェブサイト上で任意に切り替える機能を実装する場合は <meta> 要素のほうがスマート。

配色設定に基づいたスタイルの設定

配色設定に基づいてスタイルを設定するには prefers-color-scheme メディアクエリを使用する。


            .example {
              @media (prefers-color-scheme: light) {
                color: black;
                background-color: white;
              }
              @media (prefers-color-scheme: dark) {
                color: white;
                background-color: black;
              }
            }
          

または light-dark() 関数を使用することで以下のようにより簡潔に書ける。 この関数は 春頃より主要ウェブブラウザでサポートされた。 今後はほとんどの場合こちらを使うことになるだろう。


            .example {
              color: light-dark(black, white);
              background-color: light-dark(white, black);
            }
          

カラー値を直書きではなくカスタムプロパティ (変数) で管理したい場合は以下のようになる。


            @property --main-text-color {
              syntax: "<color>";
              inherits: false;
              initial-value: light-dark(black, white);
            }
            
            @property --main-background-color {
              syntax: "<color>";
              inherits: false;
              initial-value: light-dark(white, black);
            }
            
            .example {
              color: var(--main-text-color);
              background-color: var(--main-background-color);
            }
          

配色設定に基づいた画像の切り替え

<picture> 要素を使うことで配色設定に基づいた画像の切り替えが可能だ。 配色設定がダークモードの場合は <source> 要素の画像が表示され、ライトモードの場合や配色設定が未指定の場合は <img> 要素にフォールバックされる。


            <picture>
              <source srcset="dark.avif" media="(prefers-color-scheme: dark)">
              <img src="light.avif">
            </picture>
          

配色設定をウェブサイト上で任意に切り替える

ダークモードに対応したウェブサイトでは配色設定を手動で切り替えられるスイッチが実装されていることがよくある。 ここではそのベストな実装方法を解説する。 サンプル実装を次の記事で紹介しているのであわせて見てほしい。 ウェブページの配色設定 (ライトモード / ダークモード) を切り替える機能をウェブコンポーネントで実装する

以下のように CSS や <meta> 要素の color-scheme プロパティの値を light または dark に変更すればユーザーの OS やウェブブラウザの配色設定にかかわらず指定の配色設定を強制することができる。 これだけで prefers-color-scheme メディアクエリや light-dark() 関数も指定の配色設定に切り替わる。


            :root {
              color-scheme: dark;
            }
          

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

なので JavaScript で上記の書き換えを行うよう実装すればよい。 具体的な実装イメージは以下のとおりだ。

  1. ウェブサイト上に配色設定を切り替えるためのフォームコントロール ( <select> 要素など) を置く。
  2. それが操作されたら JavaScript の Web Storage API で設定値を保存。
  3. ウェブページのレンダリング時にその設定値を読み出して <meta> 要素の color-scheme プロパティを書き換える。

配色設定を <meta> 要素ではなく CSS で設定する場合は一手間増えてしまうが、 JavaScript で <html> 要素に data-color-scheme="dark" 属性などを付加し、 CSS の :root 擬似クラスの color-scheme プロパティの値を attr() 関数で data-color-scheme 属性の値から取得するようにする。


            :root {
              color-scheme: attr(data-color-scheme, "light dark");
            }
          

ただし現時点で attr() 関数をサポートしているモダンなウェブブラウザは Chrome と Edge のみで Firefox と Safari はサポートしていない ( Can I use attr()? ) ので、サポートされるまでは以下で我慢する。


            :root {
              color-scheme: light dark;
              &[data-color-scheme="light"] {
                color-scheme: light;
              }
              &[data-color-scheme="dark"] {
                color-scheme: dark;
              }
            }
          

ただこの機能が本当に必要なのかはよく検討したほうがよいだろう。 もともと OS やウェブブラウザの配色設定をダークモードにするくらいダークモードが好きな人が、特定のウェブサイトだけわざわざライトモードに切り替えたいと思うことがどれだけあるのか疑問だ。 逆にダークモードとか興味がなくデフォルトのライトモードで満足している人が特定のウェブサイトだけダークモードに切り替えたいと思うことがどれだけあるのだろうか。 ただの自己満足や技術の誇示だけにならないよう、よく検討したほうがいいだろう。

私はこの機能の提供はウェブブラウザ側の役割であり、ウェブサイト側はその要求に従うだけが本来あるべき姿だと思っている。 主要なウェブブラウザではウェブサイト毎にフォントサイズや表示倍率を変更し、履歴を消去するまでその設定値を記憶できる機能が標準搭載されているが、配色設定も同様の仕組みで対応してもらうのがベストだろう。