// Theme management — side-effect-free at import time. // Call bootstrapTheme() once from main.tsx after the React root is mounted. const MEDIA_QUERY = '(prefers-color-scheme: dark)' function getOSDark(): boolean { return window.matchMedia(MEDIA_QUERY).matches } function applyTheme(theme: 'light' | 'dark'): void { document.documentElement.dataset['theme'] = theme } // Listener reference so the media-query change handler can check localStorage // at call time (not at setup time). function handleOSChange(e: MediaQueryListEvent): void { if (localStorage.getItem('theme') !== null) return applyTheme(e.matches ? 'dark' : 'light') } /** * bootstrapTheme — reads stored preference (or falls back to OS), * applies data-theme, and registers a media-query change listener. * Call exactly once from main.tsx. */ export function bootstrapTheme(): void { const stored = localStorage.getItem('theme') const isDark = stored !== null ? stored === 'dark' : getOSDark() applyTheme(isDark ? 'dark' : 'light') window.matchMedia(MEDIA_QUERY).addEventListener('change', handleOSChange) } /** * setTheme — explicit override. * null: clears override, re-syncs with current OS preference. * 'light' | 'dark': stores override, applies immediately. */ export function setTheme(theme: 'light' | 'dark' | null): void { if (theme === null) { localStorage.removeItem('theme') applyTheme(getOSDark() ? 'dark' : 'light') } else { localStorage.setItem('theme', theme) applyTheme(theme) } }