Neat Design Journal

カラーモードを切り替えるスイッチを実装する方法

カラーモードを切り替えるスイッチを実装する方法

以前にCSSだけでダークモードに対応させる方法を投稿しました。これはこれで簡単にダークモードに対応できて良かったんですが、私のように、OSの設定はダークモードでもWebサイトはライトモードで閲覧したいと思われる方が少なからずいらっしゃるのではないかと考え、そうするとやはりカラーモードの切り替えができるスイッチを実装したいなと思った次第です。

CSSだけでダークモードに対応する方法

会社や個人、ブログやポートフォリオなどのWebサイトをCSSだけでダークモードに対応する方法をご紹介します。サイトを閲覧しているユーザーが、ダークモードとライトモードを自由に切り替えることのできるボタンを実装しているサイ […]

CSSだけでダークモードに対応する方法のファビコン
https://neatdesignjournal.com/dark-mode-css-only/
CSSだけでダークモードに対応する方法のサムネイル

上記の記事にも追記はしていますが、今回はその中身についてより詳しい解説をしたいと思います。

全体の構成

カラーモード対応をどのように行いたいかによって実装方法は変わると思うんですが、当サイトでは以下のような内容でダークモード対応を行っています。

  • サイトの初期表示は、ユーザー環境(OSの設定)に従って表示される。
  • その後は、再アクセス時やページ遷移時にカラーモードが元に戻らないように、ユーザーが選択したカラーモード情報をローカルに保存しておき、それに合わせて表示させる。

ということでまずは完成版をご覧ください。

See the Pen color-mode-switch by ryskyshd (@ryskyshd) on CodePen.

HTML

では早速中身に入っていきたいと思います。最初はカラーモードを切り替えるスイッチのHTMLから。

<button type="button" class="color_mode_switch" aria-label="カラーモードを切り替え"></button>

極力無駄な要素は省いて必要最低限のコードにするのが私のこだわりなので、切り替えスイッチ部分はbutton要素でマークアップしました。他にはinput type="checkbox"label要素を使う方法もありますが、これだと必要な要素が2つになるため採用していません。

aria-labelはアクセシビリティに対応した属性で、装飾目的などの空要素はaria-labelを付与してこの要素が一体何を表しているのかを明示します。

aria-label - ARIA | MDN

aria-label 属性は対話型要素にラベル付けする文字列値を定義します。

aria-label - ARIA | MDNのファビコン
https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label
aria-label - ARIA | MDNのサムネイル

CSS

続いてはCSSですが、まずはスイッチ部分について解説します。

button {
    all: unset;
    cursor: pointer;
    &:focus-visible {
        --outline-color: #0077e6;
        outline: 2px solid var(--outline-color);
        outline-offset: 2px;
    }
}

最初に、button要素のデフォルトスタイルをall: unset;で削除します。これでUAスタイルシートで指定されたデフォルトスタイルはほぼ全てリセットされます。そして、cursor: pointer;でマウスオーバー時の挙動を指定します。

次の&:focus-visible以降では、キーボード操作時にスイッチ自体にフォーカスが当たった時のスタイルを指定しています。all: unset;で全てのスタイルをリセットしたので、このままだとキーボード操作時にフォーカスが当たらなくなるため、アクセシビリティ的にダメな状態です。フォーカス時にoutlineを設けて要素自体から2px離して表示するようにしています。

.color_mode_switch {
    --switch-block-size: 1.5rem;
    --switch-inline-size: calc(var(--switch-block-size) * 2);
    --switch-radius: calc(infinity * 1px);
    --switch-color-light: #d99c00;
    --switch-color-dark: #3f6fd1;
    --switch-transition: var(--transition-color-mode);
    position: relative;
    inline-size: var(--switch-inline-size);
    block-size: var(--switch-block-size);
    background-color: var(--switch-color-light);
    border-radius: var(--switch-radius);
    transition: background-color var(--switch-transition);
    &::before {
        --thumb-size: var(--switch-block-size);
        --thumb-color: #fff;
        --thumb-border-size: 2px;
        position: absolute;
        inset: 0;
        display: inline-grid;
        place-content: center;
        inline-size: var(--thumb-size);
        block-size: var(--thumb-size);
        content: "\e518";
        color: var(--switch-color-light);
        font-family: "Material Icons Round";
        background-color: var(--thumb-color);
        border: var(--thumb-border-size) solid var(--switch-color-light);
        border-radius: var(--switch-radius);
        box-sizing: border-box;
        transition:
            inset-inline-start var(--switch-transition),
            border-color var(--switch-transition);
    }
    &.active {
        background-color: var(--switch-color-dark);
        &::before {
            inset-inline-start: calc(100% - var(--thumb-size));
            content: "\e51c";
            color: var(--switch-color-dark);
            border: var(--thumb-border-thickness) solid var(--switch-color-dark);
        }
    }
}

続いてはスイッチ自体のスタイルです。カスタムプロパティをこれでもかというくらい使っているので見にくい面もあるかと思いますが、よく見るとそんなに難しいことはしていません。

カスタムプロパティ部分

--switch-block-size: 2rem;
--switch-inline-size: calc(var(--switch-block-size) * 1.8);
--switch-radius: calc(infinity * 1px);
--switch-color-light: #d99c00;
--switch-color-dark: #3f6fd1;
--switch-transition: var(--transition-color-mode);

スイッチ自体の高さは2remとしていますが、これはお好みで変えてください。1remでも1rlhでもいいですし32pxでも何でも大丈夫です。また、スイッチの横幅はスイッチの高さの1.8倍としました。これもお好みで変えていただいて結構です。サイトのデザインに合うようにお好きに改変してください。

次のcalc(infinity * 1px)は錠剤型のデザインにするための呪文です。あまり深い意味は考えなくても綺麗な錠剤型になるのでまぁよしとしましょう。9999pxとか999emでもいいみたいですけどね。

次の2つはスイッチの色ですね。lightの時に黄色系、darkの時に青系の色を指定しています。ライトモード時は太陽、ダークモード時は夜をイメージした色にすると見た目的に分かりやすくなります。この辺もお好みで…(以下省略)

最後はtransitionですから、スイッチの中の丸ぽっちがスライドする時のスピードとかを指定しています。var(--transition-color-mode)としているのは、後にも出てきますがスイッチを切り替えた時に背景や文字色が変化するスピードとスイッチの丸ぽっちがスライドするスピードを一緒にするためのものです。ちなみに中身は0.25s easeとなっています。これもお好みで…(以下略)

スイッチの外側部分

position: relative;
display: block;
inline-size: var(--switch-inline-size);
block-size: var(--switch-block-size);
background-color: var(--switch-color-light);
border-radius: var(--switch-radius);
transition: background-color var(--switch-transition);

スイッチの外側部分とは、スイッチの中の丸ぽっち以外の部分と捉えていただいたら良いかと思います。丸ぽっち部分のスタイルにカスタムプロパティで定義した値をそれぞれ指定していきます。

丸ぽっち部分

&::before {
    --thumb-size: var(--switch-block-size);
    --thumb-color: #fff;
    --thumb-border-size: 2px;
    position: absolute;
    inset: 0;
    display: inline-grid;
    place-content: center;
    inline-size: var(--thumb-size);
    block-size: var(--thumb-size);
    content: "\e518";
    color: var(--switch-color-light);
    font-family: "Material Icons Round";
    background-color: var(--thumb-color);
    border: var(--thumb-border-size) solid var(--switch-color-light);
    border-radius: var(--switch-radius);
    box-sizing: border-box;
    transition:
        inset-inline-start var(--switch-transition),
        border-color var(--switch-transition);
}

丸ぽっち部分はAppleやGoogleではサム(thumb)と言われているそうなので、カスタムプロパティもそれで定義してみました。なお、サム部分はbutton要素の擬似要素(::before)で作成します。

縦横のサイズを指定してposition: absolute;inset: 0;でそれいっぱいに広がるようにしています。その他の指定は適宜いい感じの見た目になるように微調整している感じですね。この辺もお好みで…

とはいえ、なかなか一から作成するのは面倒かと思いますので、そんな時に便利なサイトをご紹介します。

細かくカスタマイズできますし、HTML、CSS、Javascript、jQueryのコードを自動で出力してくれますので、ある程度のものがこのサイトだけで完成します。便利ですね。ありがとうございます。

あとは見た目的により分かりやすくするためにGoogle Iconsを表示させてます。

スイッチ押下後のスタイル

&.active {
    background-color: var(--switch-color-dark);
    &::before {
        inset-inline-start: calc(100% - var(--thumb-size));
        content: "\e51c";
        color: var(--switch-color-dark);
        border: var(--thumb-border-thickness) solid var(--switch-color-dark);
    }
}

jQueryでスイッチを押下したら.activeというクラスを付与するようにして、それに対してスタイルを当てます。背景色を変化させ、丸ポッチ部分を反対側へスライド、アイコンを変える、ボーダカラーもこれに合わせて変化させます。

サイト全体のCSS

:root {
    --color-dark: #000;
    --color-light: #fff;
    --transition-color-mode: 0.25s ease;
}

html {
    transition: color var(--transition-color-mode), background-color var(--transition-color-mode);
    &[data-color-mode="light"] {
        color: var(--color-dark);
        background-color: var(--color-light);
    }
    &[data-color-mode="dark"] {
        color: var(--color-light);
        background-color: var(--color-dark);
    }
}

CSSの最後はサイト全体部分です。ここの指定でダークモード時、ライトモード時のスタイルを個別に指定することができます。

次に出てくるjQueryで、スイッチの切り替えごとにhtml要素にdata-color-mode="dark"またはdata-color-mode="light"を付与または切り替えるようにして、CSSでは[data-color-mode="dark"]以下にダークモード時のスタイル、[data-color-mode="light"]以下にライトモード時のスタイルを指定します。

これでCSSは完成です。

jQuery

$(function () {
    const $html = $("html");
    const savedTheme = localStorage.getItem("color-mode");
    
    if (savedTheme) {
        $html.attr("data-color-mode", savedTheme);
        if (savedTheme === "dark") $(".color_mode_switch").addClass("active");
    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
        $html.attr("data-color-mode", "dark");
        $(".color_mode_switch").addClass("active");
    } else {
        $html.attr("data-color-mode", "light");
    }
    
    $(".color_mode_switch").on("click", function () {
        $(this).toggleClass("active");
        const newMode = $(this).hasClass("active") ? "dark" : "light";
        $html.attr("data-color-mode", newMode);
        localStorage.setItem("color-mode", newMode);
    });
});

ここまで偉そうに語ってきましたが、jQueryだけはあまり詳しくないのでChatGPT先生と会話しながら自分の実装したい形になるようにコードを作成していただきました。便利ですね。

先ほどもお話ししたように、スイッチのオンオフに伴ってhtml要素にdeta-*属性の付与を行うのと、ユーザーの選択したカラーモードをローカルに保存するなどをjQueryで実装しています。

最後に

いかがでしたでしょうか。これでカラーモードの切り替えができるスイッチの実装とカラーモード対応ができたかと思います。最後にもう一度完成品を載せておきますので、コードを色々と弄ってみてください。

See the Pen color-mode-switch by ryskyshd (@ryskyshd) on CodePen.

参考サイト