近年、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サイトにもダークモード対応を導入し、より多くのユーザーにとって快適な閲覧環境を提供してみてはいかがでしょうか。