Freshでダークモード対応をする

作成日
更新日

やること

  • テーマ変更用Toggleを用意する
    • island で実装する
  • 全ページで状態の共有をする
    • Signal で実装する
  • 初期値はクライアント側の設定を利用する

Signal を用意する

signals/theme.ts
import { signal } from "@preact/signals";

export const theme = signal<"light" | "dark">("light");

Toggleを用意する

islandで実装。

islands/ThemeModeToggle.tsx
import { theme } from "../signals/theme.ts";
import { useEffect } from "preact/hooks";

export default function ThemeModeToggle() {
  // 初期値を設定する
  useEffect(() => {
    const saved = localStorage.getItem("theme");
    if (saved === "dark" || saved === "light") {
      theme.value = saved;
      document.documentElement.setAttribute("data-theme", saved);
    }
  }, []);

  // 端末側で変更された場合にも更新する
  useEffect(() => {
    const mediaQuery = globalThis.matchMedia("(prefers-color-scheme: dark)");
  
    const listener = (event: MediaQueryListEvent) => {
      const systemPrefersDark = event.matches;
      const newTheme = systemPrefersDark ? "dark" : "light";
  
      document.documentElement.setAttribute("data-theme", newTheme);
      localStorage.setItem("theme", newTheme);
    };
  
    mediaQuery.addEventListener("change", listener);
  
    return () => mediaQuery.removeEventListener("change", listener);
  }, []);

  const toggleTheme = () => {
    theme.value = theme.value === "light" ? "dark" : "light";
    document.documentElement.setAttribute("data-theme", theme.value);
    localStorage.setItem("theme", theme.value);
  };

  return (
    <button onClick={toggleTheme} class="w-12 h-12 bg-theme bg-contain lg:fixed absolute top-0 right-0 p-2 m-2">
      {theme.value === "dark" ? "" : ""}
    </button>
  );
}

Tailwindの設定

Tailwindでclassベースでダークモード切替できるようにする。

tailwind.config.ts
export default {
  darkMode: "media",
};

media を設定した場合は端末の設定に自動で追従する。 class を設定した場合は dark:bg-gray のようにしてdark が使えるようになる。

配置する

<ThemeModeToggle />

CSSの設定

色は次のようにstyle.css で変更する。

@layer base {
  :root {
    --main-text: #3c444d;
    --main-background: #f8f8fc;
  }
  :root[data-theme="dark"] {
    --main-text: #e3e3e7;
    --main-background: #3c444d;
  }
}
サイトアイコン
公開日
更新日