comparison 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
comparison
equal deleted inserted replaced
192:b818a4561a3c 193:9f4429c49733
1 import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
2
3 interface ThemeContextType {
4 isDark: boolean;
5 toggleTheme: () => void;
6 }
7
8 const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
9
10 // Apply theme class to document root
11 function applyTheme(isDark: boolean) {
12 const root = document.documentElement;
13 if (isDark) {
14 root.classList.add('dark');
15 root.classList.remove('light');
16 } else {
17 root.classList.add('light');
18 root.classList.remove('dark');
19 }
20 }
21
22 interface ThemeProviderProps {
23 children: ReactNode;
24 }
25
26 function ThemeProvider({ children }: ThemeProviderProps) {
27 const [isDark, setIsDark] = useState(() => {
28 const saved = localStorage.getItem('theme');
29 if (saved) return saved === 'dark';
30 return window.matchMedia('(prefers-color-scheme: dark)').matches;
31 });
32
33 // Apply theme on mount and change
34 useEffect(() => {
35 applyTheme(isDark);
36 localStorage.setItem('theme', isDark ? 'dark' : 'light');
37 }, [isDark]);
38
39 // Listen for system theme changes
40 useEffect(() => {
41 const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
42 const handleChange = (e: MediaQueryListEvent) => {
43 // Only apply if no explicit preference is saved
44 if (!localStorage.getItem('theme')) {
45 setIsDark(e.matches);
46 }
47 };
48
49 mediaQuery.addEventListener('change', handleChange);
50 return () => mediaQuery.removeEventListener('change', handleChange);
51 }, []);
52
53 const toggleTheme = useCallback(() => {
54 setIsDark(prev => !prev);
55 }, []);
56
57 return (
58 <ThemeContext.Provider value={{ isDark, toggleTheme }}>
59 {children}
60 </ThemeContext.Provider>
61 );
62 }
63
64 function useTheme(): ThemeContextType {
65 const context = useContext(ThemeContext);
66 if (context === undefined) {
67 throw new Error('useTheme must be used within a ThemeProvider');
68 }
69 return context;
70 }
71
72 export { ThemeProvider, useTheme };