1import { useEffect, useState } from "react";
2
3type ThemeMode = "light" | "dark" | "auto";
4
5function getInitialMode(): ThemeMode {
6 if (typeof window === "undefined") {
7 return "auto";
8 }
9
10 const stored = window.localStorage.getItem("theme");
11 if (stored === "light" || stored === "dark" || stored === "auto") {
12 return stored;
13 }
14
15 return "auto";
16}
17
18function applyThemeMode(mode: ThemeMode) {
19 const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
20 const resolved = mode === "auto" ? (prefersDark ? "dark" : "light") : mode;
21
22 document.documentElement.classList.remove("light", "dark");
23 document.documentElement.classList.add(resolved);
24
25 if (mode === "auto") {
26 document.documentElement.removeAttribute("data-theme");
27 } else {
28 document.documentElement.setAttribute("data-theme", mode);
29 }
30
31 document.documentElement.style.colorScheme = resolved;
32}
33
34export default function ThemeToggle() {
35 const [mode, setMode] = useState<ThemeMode>("auto");
36
37 useEffect(() => {
38 const initialMode = getInitialMode();
39 setMode(initialMode);
40 applyThemeMode(initialMode);
41 }, []);
42
43 useEffect(() => {
44 if (mode !== "auto") {
45 return;
46 }
47
48 const media = window.matchMedia("(prefers-color-scheme: dark)");
49 const onChange = () => applyThemeMode("auto");
50
51 media.addEventListener("change", onChange);
52 return () => {
53 media.removeEventListener("change", onChange);
54 };
55 }, [mode]);
56
57 function toggleMode() {
58 const nextMode: ThemeMode =
59 mode === "light" ? "dark" : mode === "dark" ? "auto" : "light";
60 setMode(nextMode);
61 applyThemeMode(nextMode);
62 window.localStorage.setItem("theme", nextMode);
63 }
64
65 const label =
66 mode === "auto"
67 ? "Theme mode: auto (system). Click to switch to light mode."
68 : `Theme mode: ${mode}. Click to switch mode.`;
69
70 return (
71 <button
72 type="button"
73 onClick={toggleMode}
74 aria-label={label}
75 title={label}
76 className="rounded-full border border-[var(--chip-line)] bg-[var(--chip-bg)] px-3 py-1.5 text-sm font-semibold text-[var(--sea-ink)] shadow-[0_8px_22px_rgba(30,90,72,0.08)] transition hover:-translate-y-0.5"
77 >
78 {mode === "auto" ? "Auto" : mode === "dark" ? "Dark" : "Light"}
79 </button>
80 );
81}