1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
"use client";
import { useEffect, useState } from "react";
import { resolveTheme, type Theme, type Mode } from "@/lib/themes";
const STORAGE_KEY = "app-theme";
interface Stored {
theme?: Theme;
mode?: Mode;
}
export function useTheme() {
const [theme, setThemeState] = useState<Theme>("humdrum");
const [mode, setModeState] = useState<Mode>("auto");
// hydrate from localStorage
useEffect(() => {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) {
const parsed = JSON.parse(raw) as Stored;
if (parsed.theme) setThemeState(parsed.theme);
if (parsed.mode) setModeState(parsed.mode);
}
} catch {}
}, []);
// apply whenever theme or mode change
useEffect(() => {
const applied = resolveTheme(theme, mode);
document.documentElement.dataset.theme = applied;
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ theme, mode }));
document.cookie = `app-theme=${applied}; Path=/; Max-Age=31536000; SameSite=Lax`;
} catch {}
}, [theme, mode]);
// react to system preference changes if mode === "auto"
useEffect(() => {
if (mode !== "auto") return;
const mql = window.matchMedia("(prefers-color-scheme: dark)");
const handler = () => {
document.documentElement.dataset.theme = resolveTheme(theme, "auto");
};
mql.addEventListener("change", handler);
return () => mql.removeEventListener("change", handler);
}, [theme, mode]);
return {
theme,
mode,
setTheme: setThemeState,
setMode: setModeState,
};
}
|