Mercurial
diff hg-web/src/components/theme.tsx @ 193:9f4429c49733 hg-web
[HgWeb] Making progress....
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 25 Jan 2026 20:04:55 -0800 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hg-web/src/components/theme.tsx Sun Jan 25 20:04:55 2026 -0800 @@ -0,0 +1,72 @@ +import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react'; + +interface ThemeContextType { + isDark: boolean; + toggleTheme: () => void; +} + +const ThemeContext = createContext<ThemeContextType | undefined>(undefined); + +// Apply theme class to document root +function applyTheme(isDark: boolean) { + const root = document.documentElement; + if (isDark) { + root.classList.add('dark'); + root.classList.remove('light'); + } else { + root.classList.add('light'); + root.classList.remove('dark'); + } +} + +interface ThemeProviderProps { + children: ReactNode; +} + +function ThemeProvider({ children }: ThemeProviderProps) { + const [isDark, setIsDark] = useState(() => { + const saved = localStorage.getItem('theme'); + if (saved) return saved === 'dark'; + return window.matchMedia('(prefers-color-scheme: dark)').matches; + }); + + // Apply theme on mount and change + useEffect(() => { + applyTheme(isDark); + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + }, [isDark]); + + // Listen for system theme changes + useEffect(() => { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e: MediaQueryListEvent) => { + // Only apply if no explicit preference is saved + if (!localStorage.getItem('theme')) { + setIsDark(e.matches); + } + }; + + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + }, []); + + const toggleTheme = useCallback(() => { + setIsDark(prev => !prev); + }, []); + + return ( + <ThemeContext.Provider value={{ isDark, toggleTheme }}> + {children} + </ThemeContext.Provider> + ); +} + +function useTheme(): ThemeContextType { + const context = useContext(ThemeContext); + if (context === undefined) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} + +export { ThemeProvider, useTheme };