diff --git a/src/components/ModeToggle.tsx b/src/components/ModeToggle.tsx index f764212..23cb033 100644 --- a/src/components/ModeToggle.tsx +++ b/src/components/ModeToggle.tsx @@ -11,20 +11,56 @@ import { export function ModeToggle() { const [theme, setThemeState] = React.useState< - 'theme-light' | 'dark' | 'system' - >('theme-light'); + 'light' | 'dark' | 'system' + >('system'); React.useEffect(() => { - const isDarkMode = document.documentElement.classList.contains('dark'); - setThemeState(isDarkMode ? 'dark' : 'theme-light'); + // Initialize theme from localStorage or system preference + const getStoredTheme = () => { + if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) { + const stored = localStorage.getItem('theme'); + return stored === 'dark' ? 'dark' : 'light'; + } + return 'system'; + }; + + setThemeState(getStoredTheme()); }, []); React.useEffect(() => { - const isDark - = theme === 'dark' - || (theme === 'system' - && window.matchMedia('(prefers-color-scheme: dark)').matches); - document.documentElement.classList[isDark ? 'add' : 'remove']('dark'); + const applyTheme = (newTheme: 'light' | 'dark' | 'system') => { + const isDark = newTheme === 'dark' || + (newTheme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches); + + document.documentElement.classList.toggle('dark', isDark); + + // Store actual theme preference (not 'system') + if (typeof localStorage !== 'undefined') { + if (newTheme === 'system') { + localStorage.removeItem('theme'); + } else { + localStorage.setItem('theme', newTheme); + } + } + }; + + applyTheme(theme); + + // Listen for system theme changes when in system mode + if (theme === 'system') { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = () => applyTheme('system'); + + // Use addEventListener for better Safari compatibility + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + } else { + // Fallback for older Safari versions + mediaQuery.addListener(handleChange); + return () => mediaQuery.removeListener(handleChange); + } + } }, [theme]); return ( @@ -37,7 +73,7 @@ export function ModeToggle() { - setThemeState('theme-light')}> + setThemeState('light')}> Light setThemeState('dark')}> diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 277de87..4cf00ba 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -27,15 +27,29 @@ function getThemePreference() { } return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } -const isDark = getThemePreference() === 'dark'; -document.documentElement.classList[isDark ? 'add' : 'remove']('dark'); -if (typeof localStorage !== 'undefined') { - const observer = new MutationObserver(() => { - const isDark = document.documentElement.classList.contains('dark'); - localStorage.setItem('theme', isDark ? 'dark' : 'light'); - }); - observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); +function applyTheme() { + const theme = getThemePreference(); + const isDark = theme === 'dark' || + (theme !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches); + document.documentElement.classList.toggle('dark', isDark); +} + +// Apply theme immediately to prevent flash +applyTheme(); + +// Listen for system theme changes when no explicit preference is set +if (typeof localStorage !== 'undefined' && !localStorage.getItem('theme')) { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleSystemThemeChange = () => applyTheme(); + + // Use addEventListener for better Safari compatibility + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', handleSystemThemeChange); + } else { + // Fallback for older Safari versions + mediaQuery.addListener(handleSystemThemeChange); + } }