"use client"; import { createContext, useContext, useState, useEffect, useCallback, type ReactNode, } from "react"; import { themes, getTheme, DEFAULT_THEME, type Theme } from "@/lib/themes"; interface ThemeContextValue { theme: Theme; themeName: string; setTheme: (name: string) => void; } const ThemeContext = createContext({ theme: themes[0], themeName: DEFAULT_THEME, setTheme: () => {}, }); export function useTheme() { return useContext(ThemeContext); } // Unique ID for the dynamic style element const STYLE_ID = "theme-gradient-style"; function applyTheme(theme: Theme) { const root = document.documentElement; // Toggle dark class based on theme if (theme.isDark) { root.classList.add("dark"); } else { root.classList.remove("dark"); } // Set all CSS custom properties from the theme for (const [key, value] of Object.entries(theme.vars)) { root.style.setProperty(key, value); } // Set the body background document.body.style.background = theme.bodyBg; // Set the gradient via a style element (body::before can't be styled inline) let styleEl = document.getElementById(STYLE_ID) as HTMLStyleElement | null; if (!styleEl) { styleEl = document.createElement("style"); styleEl.id = STYLE_ID; document.head.appendChild(styleEl); } styleEl.textContent = ` body::before { background: ${theme.bgGradient} !important; } `; // Store in localStorage try { localStorage.setItem("homelab-theme", theme.name); } catch { // ignore storage errors } } export function ThemeProvider({ children }: { children: ReactNode }) { const [themeName, setThemeName] = useState(DEFAULT_THEME); const theme = getTheme(themeName); // Load saved theme on mount useEffect(() => { try { const saved = localStorage.getItem("homelab-theme"); if (saved && themes.some((t) => t.name === saved)) { setThemeName(saved); } } catch { // ignore } }, []); // Apply theme whenever it changes useEffect(() => { applyTheme(theme); }, [theme]); const setTheme = useCallback((name: string) => { if (themes.some((t) => t.name === name)) { setThemeName(name); } }, []); return ( {children} ); }