diff --git a/app/components/theme-switch.tsx b/app/components/theme-switch.tsx index 03d51fb..9483b0c 100644 --- a/app/components/theme-switch.tsx +++ b/app/components/theme-switch.tsx @@ -2,91 +2,190 @@ import * as React from "react"; import { useTheme } from "next-themes"; import { ThemeProvider as NextThemesProvider } from "next-themes"; -import type { ThemeProviderProps } from "next-themes"; import { FaCircleHalfStroke } from "react-icons/fa6"; -const storageKey = 'theme-preference'; +// Key to store user preference in localStorage +const storageKey = "theme-preference"; + +// ThemeProvider component +export function ThemeProvider({ children }: { children: React.ReactNode }) { + const [mounted, setMounted] = React.useState(false); + + React.useEffect(() => { + // Ensures the component renders only after the theme is determined + setMounted(true); + }, []); + + if (!mounted) { + // Hide content until theme is mounted to prevent mismatches + return
{children}
; + } -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return ( {children} ); } +// ThemeSwitch component export const ThemeSwitch: React.FC = () => { - const { setTheme } = useTheme(); - const [mounted, setMounted] = React.useState(false); - const [currentTheme, setCurrentTheme] = React.useState<'light' | 'dark'>('light'); - - const getColorPreference = (): 'light' | 'dark' => { - if (typeof window !== 'undefined') { - const storedPreference = localStorage.getItem(storageKey); - if (storedPreference) { - return storedPreference as 'light' | 'dark'; - } - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; - } - return 'light'; - }; + const { theme, setTheme, systemTheme } = useTheme(); + const [currentTheme, setCurrentTheme] = React.useState<"light" | "dark">("light"); - const reflectPreference = (theme: 'light' | 'dark') => { - document.documentElement.classList.remove('bg-light', 'bg-dark'); - document.documentElement.classList.add(`bg-${theme}`); - setCurrentTheme(theme); - setTheme(theme); - }; + // Reflects preference based on system or manual choice + const reflectPreference = React.useCallback((newTheme: "light" | "dark") => { + document.documentElement.classList.remove("bg-light", "bg-dark"); + document.documentElement.classList.add(`bg-${newTheme}`); + setCurrentTheme(newTheme); + setTheme(newTheme); + }, [setTheme]); + // Determine initial theme preference React.useEffect(() => { - setMounted(true); - const initTheme = getColorPreference(); - reflectPreference(initTheme); + const storedPreference = localStorage.getItem(storageKey) as "light" | "dark" | null; + const systemPreference = window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const initialTheme = storedPreference || systemPreference; + reflectPreference(initialTheme); + + // Watch for system theme changes + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); const handleChange = () => { - const newTheme = mediaQuery.matches ? 'dark' : 'light'; - localStorage.setItem(storageKey, newTheme); - reflectPreference(newTheme); + const newTheme = mediaQuery.matches ? "dark" : "light"; + if (!storedPreference) reflectPreference(newTheme); }; - mediaQuery.addEventListener('change', handleChange); - - return () => mediaQuery.removeEventListener('change', handleChange); - }, [setTheme]); + mediaQuery.addEventListener("change", handleChange); + return () => mediaQuery.removeEventListener("change", handleChange); + }, [reflectPreference]); + // Toggle between themes const toggleTheme = () => { - const newTheme = currentTheme === 'light' ? 'dark' : 'light'; + const newTheme = currentTheme === "light" ? "dark" : "light"; localStorage.setItem(storageKey, newTheme); reflectPreference(newTheme); }; - if (!mounted) { - return ( -