'use client' import { createContext, useContext, useEffect, useState, ReactNode } from 'react' type Theme = 'light' | 'dark' | 'system' interface ThemeContextType { theme: Theme resolvedTheme: 'light' | 'dark' setTheme: (theme: Theme) => void toggleTheme: () => void } const ThemeContext = createContext(undefined) export function useTheme() { const context = useContext(ThemeContext) if (!context) { // Return default values when not in provider (SSR/prerendering) return { theme: 'system' as const, resolvedTheme: 'light' as const, setTheme: () => {}, toggleTheme: () => {}, } } return context } interface ThemeProviderProps { children: ReactNode defaultTheme?: Theme } export function ThemeProvider({ children, defaultTheme = 'system' }: ThemeProviderProps) { const [theme, setThemeState] = useState(defaultTheme) const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light') const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) const savedTheme = localStorage.getItem('theme') as Theme | null if (savedTheme) { setThemeState(savedTheme) } }, []) useEffect(() => { if (!mounted) return const root = document.documentElement let resolved: 'light' | 'dark' if (theme === 'system') { resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' root.removeAttribute('data-theme') } else { resolved = theme root.setAttribute('data-theme', theme) } setResolvedTheme(resolved) }, [theme, mounted]) useEffect(() => { if (!mounted) return const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const handleChange = (e: MediaQueryListEvent) => { if (theme === 'system') { setResolvedTheme(e.matches ? 'dark' : 'light') } } mediaQuery.addEventListener('change', handleChange) return () => mediaQuery.removeEventListener('change', handleChange) }, [theme, mounted]) const setTheme = (newTheme: Theme) => { setThemeState(newTheme) localStorage.setItem('theme', newTheme) } const toggleTheme = () => { const newTheme = resolvedTheme === 'light' ? 'dark' : 'light' setTheme(newTheme) } // Prevent hydration mismatch - render children without context until mounted if (!mounted) { return <>{children} } return ( {children} ) } // Theme toggle button component export function ThemeToggle() { const { resolvedTheme, toggleTheme } = useTheme() return ( ) }