| 135dfe5 | | | 1 | "use client"; |
| 135dfe5 | | | 2 | |
| 135dfe5 | | | 3 | import { createContext, useContext, useEffect, useState } from "react"; |
| 135dfe5 | | | 4 | |
| 135dfe5 | | | 5 | type Theme = "light" | "dark"; |
| 135dfe5 | | | 6 | |
| 135dfe5 | | | 7 | interface ThemeContextValue { |
| 135dfe5 | | | 8 | theme: Theme; |
| 135dfe5 | | | 9 | toggle: () => void; |
| 135dfe5 | | | 10 | } |
| 135dfe5 | | | 11 | |
| 135dfe5 | | | 12 | const ThemeContext = createContext<ThemeContextValue>({ |
| 135dfe5 | | | 13 | theme: "light", |
| 135dfe5 | | | 14 | toggle: () => {}, |
| 135dfe5 | | | 15 | }); |
| 135dfe5 | | | 16 | |
| 135dfe5 | | | 17 | export function ThemeProvider({ children }: { children: React.ReactNode }) { |
| 135dfe5 | | | 18 | const [theme, setTheme] = useState<Theme>("light"); |
| 135dfe5 | | | 19 | const [mounted, setMounted] = useState(false); |
| 135dfe5 | | | 20 | |
| 135dfe5 | | | 21 | useEffect(() => { |
| 135dfe5 | | | 22 | const stored = localStorage.getItem("grove_theme") as Theme | null; |
| 135dfe5 | | | 23 | if (stored) { |
| 135dfe5 | | | 24 | setTheme(stored); |
| 135dfe5 | | | 25 | } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { |
| 135dfe5 | | | 26 | setTheme("dark"); |
| 135dfe5 | | | 27 | } |
| 135dfe5 | | | 28 | setMounted(true); |
| 135dfe5 | | | 29 | }, []); |
| 135dfe5 | | | 30 | |
| 135dfe5 | | | 31 | useEffect(() => { |
| 135dfe5 | | | 32 | if (!mounted) return; |
| 135dfe5 | | | 33 | document.documentElement.setAttribute("data-theme", theme); |
| 135dfe5 | | | 34 | localStorage.setItem("grove_theme", theme); |
| 135dfe5 | | | 35 | }, [theme, mounted]); |
| 135dfe5 | | | 36 | |
| 135dfe5 | | | 37 | const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light")); |
| 135dfe5 | | | 38 | |
| 135dfe5 | | | 39 | // Prevent flash of wrong theme |
| 135dfe5 | | | 40 | if (!mounted) { |
| 135dfe5 | | | 41 | return <div style={{ visibility: "hidden" }}>{children}</div>; |
| 135dfe5 | | | 42 | } |
| 135dfe5 | | | 43 | |
| 135dfe5 | | | 44 | return ( |
| 135dfe5 | | | 45 | <ThemeContext.Provider value={{ theme, toggle }}> |
| 135dfe5 | | | 46 | {children} |
| 135dfe5 | | | 47 | </ThemeContext.Provider> |
| 135dfe5 | | | 48 | ); |
| 135dfe5 | | | 49 | } |
| 135dfe5 | | | 50 | |
| 135dfe5 | | | 51 | export function useTheme() { |
| 135dfe5 | | | 52 | return useContext(ThemeContext); |
| 135dfe5 | | | 53 | } |