import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react";
type Theme = "light" | "dark" | "system";
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
resolvedTheme: "light" | "dark";
}
const ThemeContext = createContext<ThemeContextType>({
theme: "light",
setTheme: () => {},
resolvedTheme: "light",
});
function getSystemTheme(): "light" | "dark" {
if (typeof window !== "undefined") {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
return "light";
}
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setThemeState] = useState<Theme>(() => {
const saved = localStorage.getItem("app-theme");
return (saved === "light" || saved === "dark" || saved === "system") ? saved : "light";
});
const resolvedTheme = theme === "system" ? getSystemTheme() : theme;
const setTheme = useCallback((t: Theme) => {
setThemeState(t);
localStorage.setItem("app-theme", t);
}, []);
useEffect(() => {
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(resolvedTheme);
}, [resolvedTheme]);
useEffect(() => {
if (theme !== "system") return;
const media = window.matchMedia("(prefers-color-scheme: dark)");
const handler = () => {
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(getSystemTheme());
};
media.addEventListener("change", handler);
return () => media.removeEventListener("change", handler);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme, resolvedTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
return useContext(ThemeContext);
}