import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'; /** * Themes: 'light' | 'dark' * Persisted to localStorage under key 'bt-theme'. * * Class mapping on document.documentElement: * light → (no classes) * dark → 'dark' */ type Theme = 'light' | 'dark'; const STORAGE_KEY = 'bt-theme'; const VALID_THEMES: Theme[] = ['light', 'dark']; const DEFAULT_THEME: Theme = 'dark'; function applyTheme(theme: Theme) { const root = document.documentElement; root.classList.remove('dark'); if (theme === 'dark') { root.classList.add('dark'); } // 'light' → no classes } function loadStoredTheme(): Theme { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored === 'dark-purple') return 'dark'; if (stored && (VALID_THEMES as string[]).includes(stored)) return stored as Theme; } catch { // localStorage unavailable } return DEFAULT_THEME; } interface ThemeContextValue { theme: Theme; setTheme: (theme: string) => void; } const ThemeContext = createContext(null); export function ThemeProvider({ children }: { children: ReactNode }) { const [theme, setThemeState] = useState(() => { const stored = loadStoredTheme(); // Apply immediately (before first paint) to avoid flash applyTheme(stored); return stored; }); const setTheme = (newTheme: string) => { if (!(VALID_THEMES as string[]).includes(newTheme)) return; applyTheme(newTheme as Theme); setThemeState(newTheme as Theme); try { localStorage.setItem(STORAGE_KEY, newTheme); } catch { // localStorage unavailable } }; // Keep DOM in sync if theme state ever changes externally useEffect(() => { applyTheme(theme); }, [theme]); return ( {children} ); } export function useTheme() { const ctx = useContext(ThemeContext); if (!ctx) { throw new Error('useTheme must be used within a ThemeProvider'); } return ctx; }