import { create } from 'zustand'; import { persist } from 'zustand/middleware'; type Theme = 'light' | 'dark' | 'system'; interface ThemeState { theme: Theme; resolvedTheme: 'light' | 'dark'; hydrated: boolean; setTheme: (theme: Theme) => void; toggleTheme: () => void; initializeTheme: () => void; } const getSystemTheme = (): 'light' | 'dark' => { if (typeof window === 'undefined') return 'light'; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }; const applyTheme = (theme: 'light' | 'dark') => { if (typeof document === 'undefined') return; const root = document.documentElement; if (theme === 'dark') { root.classList.remove('light'); root.classList.add('dark'); } else { root.classList.remove('dark'); root.classList.add('light'); } // Also update color-scheme for native elements (scrollbars, form controls) root.style.colorScheme = theme; localStorage.setItem('theme-applied', theme); }; let mediaQueryCleanup: (() => void) | null = null; export const useThemeStore = create()( persist( (set, get) => ({ theme: 'system', resolvedTheme: 'light', hydrated: false, setTheme: (theme) => { const resolvedTheme = theme === 'system' ? getSystemTheme() : theme; applyTheme(resolvedTheme); set({ theme, resolvedTheme }); }, toggleTheme: () => { const { theme } = get(); const nextTheme: Theme = theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light'; get().setTheme(nextTheme); }, initializeTheme: () => { const { theme } = get(); const resolvedTheme = theme === 'system' ? getSystemTheme() : theme; applyTheme(resolvedTheme); set({ resolvedTheme, hydrated: true }); // Clean up previous listener if any if (mediaQueryCleanup) { mediaQueryCleanup(); mediaQueryCleanup = null; } if (typeof window !== 'undefined') { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleChange = () => { const { theme } = get(); if (theme === 'system') { const newResolvedTheme = getSystemTheme(); applyTheme(newResolvedTheme); set({ resolvedTheme: newResolvedTheme }); } }; mediaQuery.addEventListener('change', handleChange); mediaQueryCleanup = () => mediaQuery.removeEventListener('change', handleChange); } }, }), { name: 'theme-storage', partialize: (state) => ({ theme: state.theme }), onRehydrateStorage: () => { return (state) => { if (state) { // Re-apply theme immediately after rehydration const resolvedTheme = state.theme === 'system' ? getSystemTheme() : state.theme; applyTheme(resolvedTheme); state.resolvedTheme = resolvedTheme; state.hydrated = true; } }; }, } ) );