46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useCallback, useEffect, useRef } from "react";
|
|
|
|
const THEME_KEY = "ttl_theme";
|
|
|
|
function getServerTheme(): "dark" | "light" {
|
|
return "light";
|
|
}
|
|
|
|
function getClientTheme(): "dark" | "light" {
|
|
if (typeof window === "undefined") return getServerTheme();
|
|
const stored = localStorage.getItem(THEME_KEY);
|
|
if (stored) return stored as "dark" | "light";
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
}
|
|
|
|
/**
|
|
* Theme toggle hook. Reads from `prefers-color-scheme` on first load, then
|
|
* respects the user's explicit choice persisted in localStorage.
|
|
*
|
|
* Returns `mounted` flag so child components can avoid hydration mismatch.
|
|
*/
|
|
export function useTheme() {
|
|
const [dark, setDark] = useState(() => getClientTheme() === "dark");
|
|
const [mounted] = useState(() => typeof window !== "undefined");
|
|
const initialized = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (!initialized.current) {
|
|
initialized.current = true;
|
|
document.documentElement.classList.toggle("dark", dark);
|
|
}
|
|
}, [dark]);
|
|
|
|
const toggle = useCallback(() => {
|
|
setDark((prev) => {
|
|
const next = !prev;
|
|
localStorage.setItem(THEME_KEY, next ? "dark" : "light");
|
|
return next;
|
|
});
|
|
}, []);
|
|
|
|
return { dark, toggle, mounted };
|
|
}
|