近年、OSやアプリケーションで広く採用されている「ダークモード」。目の負担軽減や、有機ELディスプレイでのバッテリー消費抑制といったメリットから、多くのユーザーに好まれています。
現代のWebサイトにおいて、ユーザーのOS設定を尊重し、ダークモードに自動対応することは、快適なユーザー体験を提供する上で非常に重要な要素となっています。
この記事では、基本的なCSSでの対応方法から、JavaScriptを使った高度な制御、さらにはユーザーが任意にテーマを切り替え、その設定を記憶させる方法まで、段階的かつ網羅的に解説します。
Level 1: CSSメディアクエリだけで実現する最もシンプルな方法
まず、最も手軽にOSのダークモード設定をWebサイトに反映させる方法です。
CSSのメディアクエリ prefers-color-scheme を使えば、JavaScriptを一切使わずに対応できます。
解説
prefers-color-scheme は、ユーザーがシステム(OS)で設定している外観モード(ライト/ダーク)を判別するためのメディア特性です。
これを利用して、ダークモードの場合に適用したいスタイルを記述します。
実装コード (CSS)
/* --- デフォルトのスタイル(ライトモード用) --- */body { color: #333; background-color: #fff; transition: color 0.3s, background-color 0.3s; /* スムーズな切り替え効果 */}/* --- OSがダークモードの場合に適用 --- */@media (prefers-color-scheme: dark) { body { color: #eee; background-color: #121212; }}CSSポイント:
- まず、ライトモード(デフォルト)のスタイルを定義します。
@media (prefers-color-scheme: dark)のブロック内に、ダークモード用のスタイルを上書きする形で記述します。transitionを設定しておくと、OSの設定を切り替えた際に、色が滑らかに変化し、ユーザー体験が向上します。
この方法のメリット:
- CSSだけで完結するため、非常にシンプルで軽量。
- JavaScriptが無効な環境でも動作する。
この方法のデメリット:
- あくまでOSの設定に追従するだけなので、ユーザーがサイト上で個別に「ライトモードで見たい」といった選択ができない。
Level 2: JavaScriptで外観モードを判定・制御する
次に、JavaScriptを使ってより能動的にテーマを制御する方法です。これにより、手動切り替えボタンのような、より高度な機能実装への道が開かれます。
解説
window.matchMedia() メソッドを使うことで、CSSのメディアクエリが現在の状態に一致するかどうかをJavaScriptで判定できます。'(prefers-color-scheme: dark)' を引数に渡すことで、ダークモードが有効かどうかを true / false の真偽値で取得できます。
さらに、.addListener() を使えば、ユーザーがOSの設定を変更した瞬間を検知して、リアルタイムで処理を実行することも可能です。
実装コード (JavaScript)
このコードは、OSの設定に応じて<body>タグに is-theme-light または is-theme-dark というクラスを付与します。CSSは、これらのクラスセレクタに対してスタイルを定義します。
// OSのダークモード設定を監視するconst darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');// 現在のモードに応じてクラスをbodyに付与する関数const applyTheme = (isDarkMode) => { if (isDarkMode) { document.body.classList.remove('is-theme-light'); document.body.classList.add('is-theme-dark'); } else { document.body.classList.remove('is-theme-dark'); document.body.classList.add('is-theme-light'); }};// OSの設定が変更された時に発火darkModeMediaQuery.addListener((e) => { applyTheme(e.matches);});// 初期読み込み時に一度だけ実行applyTheme(darkModeMediaQuery.matches);JavaScript対応するCSS
/* --- ライトモード用のスタイル --- */body.is-theme-light { color: #333; background-color: #fff;}/* --- ダークモード用のスタイル --- */body.is-theme-dark { color: #eee; background-color: #121212;}/* スムーズな切り替え効果 */body { transition: color 0.3s, background-color 0.3s;}CSSこの方法のメリット:
- クラスを付け替える方式なので、CSSの見通しが良くなる。
- JavaScriptでテーマの状態を管理できるため、他の処理との連携がしやすくなる。
Level 3: 手動切り替えと設定の永続化(localStorage)
最も実践的でユーザーフレンドリーな実装です。以下の要件を満たします。
- 初回アクセス時: OSの設定を自動で反映する。
- 手動切り替え: ユーザーがボタンで自由にテーマを切り替えられる。
- 設定の記憶: ユーザーが選択したテーマをブラウザの
localStorageに保存し、次回以降の訪問時もその設定を維持する。
実装コード (HTML)
テーマを切り替えるためのボタンを配置します。
<button class="js-theme-switch" aria-label="テーマを切り替える"> <span class="js-theme-icon"></span></button>HTML実装コード (JavaScript) - 完成版
これまでのロジックを統合し、localStorageへの保存・読み込み機能を追加します。
document.addEventListener('DOMContentLoaded', () => { const themeSwitch = document.querySelector('.js-theme-switch'); if (!themeSwitch) return; const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); let currentTheme = localStorage.getItem('theme-setting'); // テーマを適用し、localStorageを更新する関数 const applyTheme = (theme) => { // bodyのクラスを更新 if (theme === 'dark') { document.body.classList.add('is-theme-dark'); document.body.classList.remove('is-theme-light'); } else { document.body.classList.add('is-theme-light'); document.body.classList.remove('is-theme-dark'); } // localStorageに保存 localStorage.setItem('theme-setting', theme); currentTheme = theme; }; // 切り替えボタンのクリックイベント themeSwitch.addEventListener('click', () => { const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; applyTheme(newTheme); }); // OSの設定変更を監視 darkModeMediaQuery.addListener((e) => { // ユーザーが手動で設定していない場合のみ、OS設定に追従 if (!localStorage.getItem('theme-setting')) { applyTheme(e.matches ? 'dark' : 'light'); } }); // --- 初期読み込み時のテーマ決定ロジック --- if (currentTheme) { // 1. localStorageに設定があればそれを優先 applyTheme(currentTheme); } else { // 2. localStorageに設定がなければ、OSの設定に従う applyTheme(darkModeMediaQuery.matches ? 'dark' : 'light'); }});JavaScriptコードのポイント解説:
- 初期読み込み時の優先順位:
- まず
localStorageに'theme-setting'というキーで保存された値があるか確認します。 - 値があれば、その設定('dark' or 'light')を最優先で適用します。
- 値がなければ(初回訪問時など)、OSの設定 (
darkModeMediaQuery.matches) を見てテーマを決定します。
- まず
- クリックイベント:
- ボタンがクリックされるたびに、現在のテーマとは逆のテーマを
applyTheme関数に渡して適用します。 applyTheme関数内でlocalStorageが更新されるため、ユーザーの選択が保存されます。
- ボタンがクリックされるたびに、現在のテーマとは逆のテーマを
- OS設定変更の監視:
addListenerのコールバック内で、localStorageに手動設定が存在しない場合のみOS設定に追従するように変更しました。これにより、ユーザーが一度手動でテーマを切り替えた後は、OSの設定を変更してもサイトのテーマは上書きされず、ユーザーの選択が尊重されます。
まとめ
ダークモードへの対応は、もはや特別な機能ではなく、Webサイトの「おもてなし」の一つです。
- Level 1 (CSSのみ): 手軽に実装したい場合に最適。
- Level 2 (JSでの検知): より複雑な制御の第一歩。
- Level 3 (手動切り替え & 保存): 最高のユーザー体験を提供するための完成形。
今回紹介したコードをベースに、あなたのWebサイトにもダークモード対応を導入し、より多くのユーザーにとって快適な閲覧環境を提供してみてはいかがでしょうか。