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

CSSとJSを使ったダークモード

Webサイトでダークモードを設定したい場合、「prefers-color-scheme」を使ったやり方がよく知られています。ですが、このやり方だと、デバイスの設定に依存してしまうので、『デバイスはライトモードだけどサイトはダークモード』、もしくはその逆の『デバイスはダークモードでサイトはライトモード』というのは、出来ないんじゃないかなと思っています(実は、自分で検証していないので、もし出来るようだったら、ゴメンナサイ)。

そこで、あまり使うものではないかもしれませんが、二つのカラーモードを、デバイスの設定に依存しないように、CSSとJSで作ってみたいと思います。

擬似クラス「:root」を使った配色の変更

基本的な考え方は「prefers-color-scheme」と同じで、二つのモードのCSSを用意します。ただ、その振り分けを、@mediaではなくて、bodyタグにclassがあるかどうかで判断します。

画像をクリックすると、サンプルページが開きます。下の方のbuttonをクリックすると、背景色を文字色が変わります。

どんな仕組みになっているかというと、まず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を使ってみました。

画像をクリックすると、サンプルページが開きます。隣のaタグをクリックしても背景色は保持されるはず。


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などで、あるコンテンツが表示されたら、そのテーマにあった配色にするといったときに使えそうかなと思います。

関連記事