import { useState, useEffect, useCallback } from 'react'; export type Theme = 'light' | 'dark'; export interface UseThemeOptions { /** localStorage key — default: 'theme' */ storageKey?: string; /** Apply theme via class or data-theme attribute — default: 'class' */ attribute?: 'class' | 'data-theme'; } export interface UseThemeReturn { theme: Theme; setTheme: (t: Theme) => void; toggleTheme: () => void; } function readStoredTheme(key: string): Theme { if (typeof window === 'undefined') { return 'dark'; } const stored = window.localStorage.getItem(key); return stored === 'light' || stored === 'dark' ? stored : 'dark'; } function applyTheme(theme: Theme, attribute: 'class' | 'data-theme') { if (typeof document === 'undefined') { return; } const root = document.documentElement; if (attribute === 'data-theme') { root.setAttribute('data-theme', theme); } else { root.classList.remove('light', 'dark'); root.classList.add(theme); } } export function useTheme(options?: UseThemeOptions): UseThemeReturn { const storageKey = options?.storageKey ?? 'theme'; const attribute = options?.attribute ?? 'class'; const [theme, setThemeState] = useState(() => readStoredTheme(storageKey)); // Apply theme to DOM whenever it changes useEffect(() => { applyTheme(theme, attribute); }, [theme, attribute]); // Sync across tabs via storage event useEffect(() => { if (typeof window === 'undefined') return; function handleStorage(event: StorageEvent) { if (event.key !== storageKey) { return; } if (event.newValue === 'light' || event.newValue === 'dark') { setThemeState(event.newValue); } } window.addEventListener('storage', handleStorage); return () => window.removeEventListener('storage', handleStorage); }, [storageKey]); const setTheme = useCallback( (t: Theme) => { setThemeState(t); if (typeof window !== 'undefined') { window.localStorage.setItem(storageKey, t); } }, [storageKey] ); const toggleTheme = useCallback(() => { setThemeState(prev => { const next: Theme = prev === 'dark' ? 'light' : 'dark'; if (typeof window !== 'undefined') { window.localStorage.setItem(storageKey, next); } return next; }); }, [storageKey]); return { theme, setTheme, toggleTheme }; }