Webでダークモードに対応する

Webでダークモードに対応する

このウェブサイトは何回かのアップデートを重ねており、以下で紹介するスクリーンショットの中には古いものがあります。ですが技術的な内容そのものには変更ありません。

axross.devをダークモードに対応させました。

ダークモードとは

ダークモードはユーザーのOS・ブラウザの設定または端末に応じて表示を黒背景に変える仕組みです。iOS、Android、macOS、WindowsなどのOSの設定でダークモードに設定している人は既にこのページが黒背景で表示されていると思います。ダークモードをオフにすると白背景で表示されます。

ダークモードに対応しました
ダークモードに対応しました

ダークモードはアクセシビリティの機能

ダークモードは単なる「Webサイトの色設定」ではありません。ユーザーと端末が求めている色でWebサイトを表示する機能と考えることができます。

OSのUIが全体的に黒背景を貴重としているのにWebサイトだけ白背景なのは不自然です。また、黒背景で慣れた目に急に白背景が飛び込んでくるとダメージを感じるかもしれません。

macOS Catalinaでのダークモードの設定
macOS Catalinaでのダークモードの設定

最新のiOSやmacOSの設定には日の入りから日の出まで自動的にダークモードをオンにするオプションがあります。多くのモバイルデバイスのOSに搭載されているNight LightやNight Shift (ブルーライトの軽減を目的として時間帯によってディスプレイの色味を変える機能) と同じように生活に馴染んでいくと思います。

ダークモードはユーザーの電池持ちを良くする

昨今のモバイルデバイスにはディスプレイに有機ELを使っているものが多いです。有機ELはバックライトを用いずドットごとに素子が発光するので、暗い色ほど消費エネルギーが少なくなります。Googleの調査によるとダークモードの利用によって端末の電池消費を50%以上削減できた例もあります。

有機EL時代には「ダークモード」を積極採用すべき──Googleが開発者に呼びかけ - Engadget 日本版
Googleは、11月7日~8日に開催されたAndroidの開発者会議 Android Dev Summit'18において、ダークモード(ナイトモード)が消費電力的に優れており、アプリ開発者に採用を呼び掛ける内容のセッションを行いました。 全体の色調を暗くするダークモードは、夜間など薄暗い環境で目に優しいとして多くのアプリが採用しています。また、単に目に優しいだけではなく、有機ELでは消費電力に優れているとも言われてきました。
http://japanese.engadget.com/jp-2018-11-09-el-google.html

ダークモードを実装する

メディアクエリで prefers-color-scheme を利用するとダークモードがオンになっているかどうかを判別できます。次のコードはダークモードがオンになっている時は背景が #000 、オフの時は #fff になります。

html { background-color: #fff; } @media (prefers-color-scheme: dark) { html { background-color: #000; } }

ダークモードを実装する時はまずカラースキームを作りましょう。実際には背景だけではなくテキストやアイコンの色、ボタンやテキスト入力部品の背景色やボーダー、UI部品によってはドロップシャドウの色など、それまで白背景を貴重に作られていたすべての色に対して、黒背景用のものを用意しましょう。

JavaScriptで判別する

メディアクエリなので window.matchMedia() で判別できます。

// ダークモードならtrue、そうでなければfalse window.matchMedia("(prefers-color-scheme: dark)").matches

ダークモードのオン・オフの切り替えを監視するには、 window.matchMedia() から得られる MediaQueryList.addEventListener() を利用します。次のコードはReactのContextを通じてダークモードかどうかを子孫コンポーネントに伝搬するコードです。

const DarkModeContext = React.useContext(); function DarkModeProvider({ children }) { const [isDarkMode, setIsDarkMode] = React.useState(false); React.useEffect(() => { // "prefers-color-scheme: dark" に対するMediaQueryListを取得 const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); // 次の行で使うイベントハンドラ // ダークモードがオンならe.matchesはtrueになり、オフならfalseになる const listener = e => setDarkMode(e.matches); // ダークモードのオン・オフが切り替わった時にchangeイベントが発火される mediaQueryList.addEventListener("change", listener); // このコンポーネントがレンダーツリーから外れた時にはchangeイベントへの監視を解除する () => mediaQueryList.removeEventListener("change", listener); }); // Contextを通じて子孫要素にダークモードかどうかを伝搬する return ( <DarkModeContext.Provider value={isDarkMode}> {children} </DarkModeContext.Provider> ); } function SomeComponent() { // コンテキストを通じてダークモードかどうかを判別する const isDarkMode = React.useContext(DarkModeContext); return isDarkMode ? <AnotherDarkComponent /> : <AnotherLightComponent />; }

JavaScriptのみでダークモードを実装するのはできるだけ控えてください。なぜならダークモードは端末やOSの設定に依存し、サーバーサイドレンダリングの際にその情報を取得することができず、サーバーでのレンダリング結果とリハイドレート後のレンダリングが異なる結果になってしまうからです。

できるだけCSSのメディアクエリ (@media) を利用してダークモードを実装するのをおすすめします。

prefers-color-scheme の対応状況

モダンブラウザはEdgeを除き対応しています。例のごとくInternet Explorerは非対応です。

prefers-color-schemeの対応状況
prefers-color-schemeの対応状況

関連リンク

prefers-color-scheme: Hello darkness, my old friend
Many devices now support an operating system wide dark mode or dark theme experience. This post explains how dark mode can be supported on web pages, lists best practices, and introduces a custom element named dark-mode-toggle that allows web developers to offer users a way to override their operating system level preference on specific web pages.
https://web.dev/prefers-color-scheme/