- Web制作
- CSSとJSを使ったダークモード
CSSとJSを使ったダークモード

Webサイトでダークモードを設定したい場合、「prefers-color-scheme」を使ったやり方がよく知られています。ですが、このやり方だと、デバイスの設定に依存してしまうので、『デバイスはライトモードだけどサイトはダークモード』、もしくはその逆の『デバイスはダークモードでサイトはライトモード』というのは、出来ないんじゃないかなと思っています(実は、自分で検証していないので、もし出来るようだったら、ゴメンナサイ)。
そこで、あまり使うものではないかもしれませんが、二つのカラーモードを、デバイスの設定に依存しないように、CSSとJSで作ってみたいと思います。
擬似クラス「:root」を使った配色の変更
基本的な考え方は「prefers-color-scheme」と同じで、二つのモードのCSSを用意します。ただ、その振り分けを、@mediaではなくて、bodyタグにclassがあるかどうかで判断します。

どんな仕組みになっているかというと、まずCSSはこんな風に設定しています。
:root{
--bg-color: #fff;
--font-color: #000;
}
:root .dark {
--bg-color: #000;
--font-color: #fff;
}
body{
background-color: var(--bg-color);
}
p{
color: var(--font-color);
}
次に、JSを使って、クリックしたときに、bodyタグに対してclass「dark」の追加・削除を行います。
window.onload = function(){
let body = document.querySelector("body");
let btn = document.querySelector("button");
btn.addEventListener('click', function(){
body.classList.toggle("dark");
});
}
また、「prefers-color-scheme」を使った場合は、サイトを閲覧しているときに、配色を切り替えるというのは大変そうですが、このやり方はclassの有無で判断しているので、リロードなどしなくても、ボタンをクリックするだけで背景色を変えることができます。
あと、classのパターンとbuttonの数を増やせば、グレーモードや淡い系の色モードなど、複数の配色パターンを実装できると思います。
ページ遷移の際に設定がリセットされてしまう
ただ、このやり方には弱点があって、他のページに遷移すると新しくページがロードされるので、bodyタグのclassは消えてしまいます。ですので、最初のページでダークモードを選んだとしても、他のページに遷移した途端に、最初の状態になってしまうんですね。
先ほどのサンプルページでは、buttonタグの横にあるaタグをクリックすると、次のページで色が戻ってしまうのを確認できると思います。
設定保持するためにLocalStorageを使用
今回のようなページをまたぐときに設定を保持するには、何らかの形でデータを保持する必要があるのですが、今回はLocalStorageを使ってみました。

buttonをクリックしたときに、classを追加するのと一緒に、LocalStorageにモードの情報を追記します。
window.onload = function () {
let body = document.querySelector("body");
let btn = document.querySelector("button");
let colorMode = window.localStorage.getItem('colorMode');
if (colorMode !== null && colorMode == "on") {
body.classList.add("dark");
}
btn.addEventListener('click', function () {
if (colorMode == "on") {
window.localStorage.setItem('colorMode', 'off');
} else {
window.localStorage.setItem('colorMode', 'on');
}
body.classList.toggle("dark");
});
}
このLocalStorageは、PCに保存されるので、例えばどのPCやスマホで見ても同じモードにしたい場合は、会員管理と連動させる必要があり、チョット難しくなりそうです。
「:root」を使った他のサンプル
「:root」を使った他のサンプルとして、ある程度スクロールすると背景色が変わるページを作ってみました。

このサンプルでは、200px分スクロールするとboduタグにclass「dark」を追加して、600pxより下に進むとclassを削除しています。条件分岐は、もっとスマートな書き方がありそうですが、今回はあまり最適化を考えずに設定しています。
window.addEventListener('scroll', function () {
let startDark = -200;
let endDark = -600;
let offsetLength = body.getBoundingClientRect().top;
if (offsetLength < startDark && offsetLength > endDark) {
body.classList.add("dark");
} else {
body.classList.remove("dark");
}
});
このサンプルは、タイミングは特に意識していないのですが、例えばLPなどで、あるコンテンツが表示されたら、そのテーマにあった配色にするといったときに使えそうかなと思います。