()
| 18 | }); |
| 19 | |
| 20 | function App() { |
| 21 | const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); |
| 22 | const [mode, setMode] = useState(null); |
| 23 | const activeMode = mode ?? (prefersDarkMode ? 'dark' : 'light'); |
| 24 | useEffect(() => { |
| 25 | let mounted = true; |
| 26 | localforage.getItem('theme').then((theme) => { |
| 27 | if (mounted && theme) { |
| 28 | setMode(theme); |
| 29 | } |
| 30 | }); |
| 31 | return () => { |
| 32 | mounted = false; |
| 33 | }; |
| 34 | }, []); |
| 35 | |
| 36 | useEffect(() => { |
| 37 | const root = document.documentElement; |
| 38 | if (activeMode === 'dark') { |
| 39 | root.style.setProperty('--scrollbar-color', 'rgba(155, 155, 155, 0.7)'); |
| 40 | root.style.setProperty('--bg-color', '#0F1318'); |
| 41 | root.style.setProperty('--link-color', '#6BA6E8'); |
| 42 | } else { |
| 43 | root.style.setProperty('--scrollbar-color', 'rgba(0, 0, 0, 0.5)'); |
| 44 | root.style.setProperty('--bg-color', '#E8EDF5'); |
| 45 | root.style.setProperty('--link-color', '#1C5BAA'); |
| 46 | } |
| 47 | }, [activeMode]); |
| 48 | |
| 49 | const toggleTheme = useCallback(() => { |
| 50 | const nextMode = activeMode === 'dark' ? 'light' : 'dark'; |
| 51 | setMode(nextMode); |
| 52 | localforage.setItem('theme', nextMode); |
| 53 | }, [activeMode]); |
| 54 | const theme = useMemo(() => createTheme({ |
| 55 | palette: { |
| 56 | mode: activeMode, |
| 57 | contrastThreshold: 3, |
| 58 | primary: { |
| 59 | main: activeMode === 'dark' ? '#6BA6E8' : '#1C5BAA', |
| 60 | light: activeMode === 'dark' ? '#93C0F2' : '#5189C9', |
| 61 | dark: activeMode === 'dark' ? '#4F86CE' : '#14457F', |
| 62 | contrastText: activeMode === 'dark' ? '#0B1B2E' : '#ffffff', |
| 63 | }, |
| 64 | secondary: { |
| 65 | main: activeMode === 'dark' ? '#9AA4B2' : '#55606C', |
| 66 | contrastText: activeMode === 'dark' ? '#0B1B2E' : '#ffffff', |
| 67 | }, |
| 68 | background: { |
| 69 | default: activeMode === 'dark' ? '#0F1318' : '#E2E8F2', |
| 70 | paper: activeMode === 'dark' ? '#181D25' : '#ECF1F9', |
| 71 | }, |
| 72 | text: { |
| 73 | primary: activeMode === 'dark' ? '#E7EBF1' : '#1A2027', |
| 74 | secondary: activeMode === 'dark' ? '#97A1AF' : '#55606C', |
| 75 | }, |
| 76 | divider: activeMode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(16,24,40,0.08)', |
| 77 | // App-level surface tiers. Light mode is a soft blue-tinted off-white family |
nothing calls this directly
no outgoing calls
no test coverage detected