個人的なサイトで使うために作ったものですが、もしかしたら使う人もいるかなということで流用しやすいように整理して公開してみました。Tailwind CSS を使用することで CSS は一切書かずにダークモード対応します。
ソースコードは下記。
動作デモは下記。
Astro で使いやすいようにコンポーネントにしたものと、Pug の Mixin にしたものを参考に入れていますので、使いやすい方を使ってもらえれば。
ソースコード解説
たいして難しいことはやっていないので、ソースコード見ればわかると思いますが、一応使い方含めて簡単に解説。
まず、前提として Tailwind CSS の使用が必須になりますが、加えて、ダークモード設定を下記のように加えてください。
module.exports = {
darkMode: 'class',
// ...
}
これにより、ライトモード / ダークモードの切り替えが、prefers-color-scheme メディアクエリではなく、HTML 側に付与される class 属性によって行われるようになります。
<!-- ライトモードの場合 -->
<html>
<body>
<!-- 背景は bg-white が適用される -->
<div class="bg-white dark:bg-black">
<!-- ... -->
</div>
</body>
</html>
<!-- ダークモードの場合(初期設定では親要素に class="dark" が付与された場合) -->
<html class="dark">
<body>
<!-- 背景は bg-black が適用される -->
<div class="bg-white dark:bg-black">
<!-- ... -->
</div>
</body>
</html>
で、具体的には親要素に class="dark" を付与 (初期設定。tailwind.config.js で変更可能) すればダークモード用のユーティリティクラス (e.g. dark:bg-black) が適用されることになりますので、ダークモード切り替えスイッチ側ではこの class="dark" を JavaScript でコントロールしてあげるだけで簡単に実現することができます。
ということで実際の切り替えスイッチ、及び JavaScript 部分。
実装の仕方は色々考えられると思います。チェックボックス (<input type="checkbox">) を使用する方法もあると思いますが、今回はライトモード、ダークモードそれぞれにボタンを作って、押した方で切り替わる形にしてみました。
切り替えスイッチの HTML 部分は下記のような感じ。アイコンは Heroicons を使用。
<div class="flex h-7 w-14 rounded-full bg-gray-100 dark:bg-gray-900">
<span class="sr-only">現在のモード:
<span id="darkmode-state">ライトモード</span>
</span>
<div class="flex justify-between items-center w-full">
<div class="flex justify-center items-center h-6 w-6">
<button class="flex justify-center items-center h-6 w-6 rounded-full bg-yellow-300 text-gray-900 dark:bg-transparent dark:text-gray-200" id="handle-darkmode-off" type="button">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z">
</path>
</svg><span class="sr-only">ライトモードに切り替える</span>
</button>
</div>
<div class="flex justify-center items-center h-6 w-6">
<button class="flex justify-center items-center h-6 w-6 rounded-full bg-transparent text-gray-900 dark:bg-white text dark:text-gray-900" id="handle-darkmode-on" type="button">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg><span class="sr-only">ダークモードに切り替える</span>
</button>
</div>
</div>
</div>
で、JavaScript は下記のような感じ。
前述の通り、class="dark" を切り替えてあげればいいので、html 要素に対して classList.add したり classList.remove するだけの簡単なお仕事。状態維持のためにはローカルストレージを使用します。
window.addEventListener("DOMContentLoaded", () => {
const lightBtn = document.getElementById("handle-darkmode-off");
const darkBtn = document.getElementById("handle-darkmode-on");
const state = document.getElementById("darkmode-state");
const stateLightText = "ライトモード";
const stateDarkText = "ダークモード";
// ライトモードスイッチが押されたときの処理
const handleDarkmodeOff = () => {
localStorage.setItem("prefers_color_scheme_set", "light");
document.documentElement.classList.remove("dark");
state.textContent = stateLightText;
};
// ダークモードスイッチが押されたときの処理
const handleDarkmodeOn = () => {
localStorage.setItem("prefers_color_scheme_set", "dark");
document.documentElement.classList.add("dark");
state.textContent = stateDarkText;
};
// ローカルストレージを確認して処理
if (localStorage.prefers_color_scheme_set === "dark" || (!("prefers_color_scheme_set" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark");
state.textContent = stateDarkText;
} else {
document.documentElement.classList.remove("dark");
state.textContent = stateLightText;
}
// ボタンにイベントリスナを登録
lightBtn.addEventListener("click", handleDarkmodeOff);
darkBtn.addEventListener("click", handleDarkmodeOn);
});
ということで、もし Tailwind CSS を使用していて、ダークモード切り替えスイッチが使いたいなと思ったときは参考にして頂ければ幸いです。