2024-03-02
daisyUIを利用したテーマの切り替えを実装する。
テーマ変更の実装
普段はダークモードでしか見ないので,実装する必要があるかどうか悩んだが,デザインの検証をする際により楽になると思って実装。
また,アクセシビリティの一つとしてあればいいなと思った。
参考サイト
実装
DaisyUIの導入
Tailwindで利用することができるクラスを定義するだけでコンポーネントを扱える。
詳しくは参考サイトを参照。
bun add -D daisyui@latest
tailwind.config.cjs
デフォルトのMediaの場合はイベントで変更することができないため,クラスを用いてハンドリングできるようにしておく。
darkMode: "class",
plugins: [require("daisyui")],
レンダリング時にテーマを読み取る
サーバーサイドと違い,クライアント側で処理するため初期表示の際にどうしても一瞬ダークモードに設定していた場合はラグが発生することになる。
TailwindでのデフォルトのMediaに設定しておけば事前にブラウザ側の設定でダークモードかどうかが判定することができるが今回のようにクラスを利用する場合は一度レンダリングのタイミングで設定を見に行く必要があるためその時間のラグがある。
できる限りの対処法としてスクリプトをHeadタグに埋め込み、HTMLが完全に解析される前にテーマを適用する方法によって、ページ読み込み前にできる限りの最速のアプローチを取ることはできる。
Layout.astro
headタグ内のなるべく上位に配置。
まず注意する点として,
<script>
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.classList.add("dark");
}
</script>
のように記述してしまうとこのサイト自体がSPAではないため,毎回違うページに遷移するたびにHeadタグを読み込んでいく。
そのため
<script>
const theme = localStorage.getItem("theme");
if (theme) {
if (theme === "dark") {
document.documentElement.classList.add("dark");
}
} else {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
}
}
</script>
こんな感じにローカルストレージをまずは参照することが必要。
そのため,この設定においてはSwapアイコンのクリックによってテーマの変更を行ってもLocalstorageを読み込んでいないためブラウザのテーマ依存によって設定していても読み込まれなくなる。
Swapボタンの実装
SwapThemeButton.astro
---
---
<!-- TODO: add swap button component -->
<script>
document.addEventListener("DOMContentLoaded", () => {
const switcher: HTMLInputElement | null = document.getElementById(
"theme-switcher",
) as HTMLInputElement;
const currentTheme: string | null = localStorage.getItem("theme");
if (currentTheme === "dark") {
switcher.checked = true;
} else {
switcher.checked = false;
}
switcher.addEventListener("change", function (): void {
if (this.checked) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
} else {
document.documentElement.classList.remove("dark");
localStorage.setItem("theme", "light");
}
});
});
</script>
また,テーマ設定をダークにしていてもSwapアイコンでライトにしていた場合はページを離れて再度訪れたとしても以前のローカルストレージが残っていればそのまま以前の設定通りのテーマ表示をすることができる。
クライアントサイドの限界
通常のメディアを使う場合のようなラグなくテーマの設定を行うことは厳しい。
対処法としてそもそもSSRを利用するか,もしくは初期表示のみメディアを参照し,それ以降はクラスを参照してテーマ設定を行うことも可能だが,どうしてもTailwindを利用している特性上手間が発生してしまい統一性を取ることができなくなり,煩雑になりかねないと感じた。
結果的に今回はどこまで個人的に現在のラグがあるバージョンに耐えれるかを検証して,やはりラグなく表示できるようにしたかったらロールバックしたいと考えている。
またテーマの設定以外のものに関してはデザインが変化するだけなので下位のコンポーネントで処理しても問題ないと考えている。