JavaScriptでダークモードやライトモードのテーマカラーを判別する

PCやスマートフォン、タブレット端末や各Webブラウザでは、ダークモードやライトモードなどのテーマカラーの設定があり、OSの設定に合わせて自動でブラウザのテーマカラーを設定していたり、手動でどちらかのテーマカラーに設定したりと環境は様々です。
Web制作では、必要に応じてこれらのテーマカラーに合わせてWebデザインを作っていく必要もできてきます。
テーマカラーでの配色を含むデザインの切り替えにおいては、CSSのみでもユーザーが利用している環境のテーマカラーを判別することはできますが、CSSの能力を超える、より動的な制御や外部との連携が必要な場合には、JavaScriptが必要になります。
例えば、Webサイトのロゴやコンテンツ画像など、単なる装飾ではなく「コンテンツ」としてタグで配置されている画像を、テーマに応じて切り替えたい場合や、ユーザーによる手動のテーマ切り替え機能を実装する場合、CSSのみでの判別では対応できないこともあります。
また、グラフ描画ライブラリ(Chart.js)や地図ライブラリ(Google Maps)など、外部ライブラリ使ってコンテンツを作成している場合は、ライブラリ独自のDOM構造でコンテンツを生成することが多いため、CSSでの配色の変更は難しく、JavaScriptのオプションで指定する必要があります。
ここでは、JavaScriptでダークモードとライトモードのテーマカラーによるスタイルの切り替えについて、サンプルコードを交えてご紹介します。
まずは簡単なHTML構造を用意しておきます。
HTML
<main>
<div class="card">
<h2>カードのタイトル 1</h2>
<p>カード内のコンテンツです。</p>
<p>表示モードに合わせて背景色や文字色が変わります。</p>
<p><a href="#">サンプルリンク</a></p>
</div>
<div class="card">
<h2>カードのタイトル 2</h2>
<p>カード内のコンテンツです。</p>
<p>表示モードに合わせて背景色や文字色が変わります。</p>
</div>
</main>
この後にご紹介するサンプルでは、上記のHTML構造は共通のものとします。
ちなみに、CSSのみでテーマカラーを判別する場合は、下記のようにメディアクエリを使って、「prefers-color-scheme」のメディア特性に適用するスタイルを定義します。
CSS
:root {
--text-color: #222;
--background-color: #f8f9fa;
--card-background: #ffffff;
--link-color: #007bff;
}
@media (prefers-color-scheme: dark) {
:root {
--text-color: #e9ecef;
--background-color: #121212;
--card-background: #1e1e1e;
--link-color: #64b5f6;
}
}
body {
color: var(--text-color);
background-color: var(--background-color);
transition: all 0.3s;
padding: 2rem;
}
main {
max-width: 800px;
margin: 0 auto;
}
a {
color: var(--link-color);
}
.card {
background-color: var(--card-background);
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 1.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
メディアクエリでは、「prefers-color-scheme: dark」として、ダークモードの際にCSSの変数(カスタムプロパティ)で一元管理している配色をダークモード用に上書きし、それぞれの要素に適用している配色に反映させています。
(上:ライトモード 下:ダークモード)

Webブラウザによるダークモードとライトモードの切り替えの確認は、デベロッパーツールで手軽に切り替えて確認することができます。
デベロッパーツールは、Windowsは「Ctrl + Shift + I」または「F12」、Macは「Command + Option + I」のショートカットキーで手軽に起動できます。
「Elements」タブのスタイルの表示にて、色の設定アイコンから prefers-color-scheme を light または dark に設定して表示を切り替えます。

前述したとおり、CSSだけでは対応できない場合は、JavaScriptでテーマカラーを判別してスタイルを切り替える必要があります。
JavaScriptでテーマカラーを判別する
JavaScriptでテーマカラーを判別してCSSでスタイルを適用させる場合は、HTML要素にdata-theme属性やclass属性を付与して実装する方法が一般的です。
まずは、data-theme属性を付与する方法を見ていきます。
テーマカラーを判別してdata-theme属性を付与
CSSでは、:root でサイト全体で使うグローバルなCSS変数を定義し、ライトモードのスタイルに適用する配色を指定する中、属性セレクタ [data-theme=”dark-mode”] で「data-themeという属性の値がdark-modeである要素」にスタイルを適用させ、ダークモード用の配色に上書きします。
CSS
:root {
--text-color: #222;
--background-color: #f8f9fa;
--card-background: #ffffff;
--link-color: #007bff;
}
[data-theme="dark-mode"] {
--text-color: #e9ecef;
--background-color: #121212;
--card-background: #1e1e1e;
--link-color: #64b5f6;
}
body {
color: var(--text-color);
background-color: var(--background-color);
transition: all 0.3s;
padding: 2rem;
}
main {
max-width: 800px;
margin: 0 auto;
}
a {
color: var(--link-color);
}
.card {
background-color: var(--card-background);
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 1.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
対象の要素のプロパティに指定しているCSS変数の値が上書きされ、ダークモード用の配色としてスタイルが適用されます。
transitionプロパティでは、0.3秒かけて滑らかに変化するようにしています。
JavaScriptでは、matchMediaメソッドを使うことで、テーマカラーの判別を行うことができます。
matchMediaメソッドは、指定されたメディアクエリ文字列のパース結果を、MediaQueryListオブジェクトとして返します。
以下、サンプルコードになります。
JavaScript
const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const updateTheme = (event) => {
const isDark = event.matches;
document.documentElement.setAttribute('data-theme', isDark ? 'dark-mode' : 'light-mode');
};
updateTheme(mediaQueryList);
mediaQueryList.addEventListener('change', updateTheme);
「window.matchMedia(‘(prefers-color-scheme: dark)’)」のメディアクエリリストを取得では、ブラウザに「OSやブラウザの設定はダークモードですか?」と問い合わせて、その監視オブジェクトを変数 mediaQueryList に保存しています。
matchMediaメソッドが返した結果から、.matchesで true か false を変数 isDark に格納し、DOM操作で.setAttributeを使って、data-theme属性に dark-mode か light-mode かを設定します。
現在ダークモードであれば、matchesにtrue、そうでなければfalseが返ってきます。

テーマを更新する処理を実装するupdateTheme関数は、初期読み込み時に一度実行します。
そのあとユーザーが設定を変更する場合には、変更があった時に関数を実行するようにしています。
実装後、デベロッパーツールでは、html要素にdata-theme属性が付与され、dark-mode とテーマカラーが指定されているのが確認できます。
またCSSのスタイルも、data-theme属性に対するスタイルが適用されているのが確認できます。

テーマカラーを判別してclass属性を付与
次に、class属性を付与する方法を見ていきます。
CSSは、上記のdata-theme属性の部分を、classセレクタの指定に変更するだけとなります。
classセレクタ「.dark-mode」にスタイルを適用させるようにして、ダークモード用の配色に上書きします。
CSS
:root {
--text-color: #222;
--background-color: #f8f9fa;
--card-background: #ffffff;
--link-color: #007bff;
}
body.dark-mode {
--text-color: #e9ecef;
--background-color: #121212;
--card-background: #1e1e1e;
--link-color: #64b5f6;
}
body {
color: var(--text-color);
background-color: var(--background-color);
transition: all 0.3s;
padding: 2rem;
}
main {
max-width: 800px;
margin: 0 auto;
}
a {
color: var(--link-color);
}
.card {
background-color: var(--card-background);
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 1.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
ユーザーの環境がダークモードであれば、classセレクタ「.dark-mode」に指定しているCSS変数の値に上書きされ、ダークモード用の配色として、対象の要素にスタイルが適用されます。
JavaScriptも、上記のdata-theme属性の付与の場合と流れはほとんど変わらず、matchMediaメソッドを使ってテーマカラーを判別し、返ってきた結果をもとにclass属性に「dark-mode」または「light-mode」の値を設定します。
JavaScript
const mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');
const updateTheme = (event) => {
const isDark = event.matches;
document.body.setAttribute('class', isDark ? 'dark-mode' : 'light-mode');
};
updateTheme(mediaQueryList);
mediaQueryList.addEventListener('change', updateTheme);
DOM操作では「document.body.setAttribute()」として、body要素にclass属性を付与しています。
実装後、デベロッパーツールでは、body要素にclass属性が付与され、dark-mode とテーマカラーが指定されているのが確認できます。
またCSSのスタイルも、対象のclassに対するスタイルが適用されているのが確認できます。

まとめ
ダークモードとライトモードの判別からのスタイルの適用は、CSSのみでも可能ですが、Webサイトの作りによってはJavaScriptでの判別が必要になることもあります。
JavaScriptでは、matchMediaメソッドを使うことで、テーマカラーの判別を行うことができます。これにより、data-theme属性やclass属性を付与して、CSSでのスタイル調整が可能になります。
また、matchMediaメソッドについては、メディアクエリや表示領域のリサイズの判定、hover動作のデバイスの判別など、様々なことが可能です。
以下の記事で使い方をご紹介していますので、ぜひ参考にしてください。