Spaces:
Running
Running
| import { createContext, useContext, useState, useEffect, useCallback } from 'react'; | |
| const ThemeContext = createContext(); | |
| export function ThemeProvider({ children }) { | |
| const [theme, setTheme] = useState(() => { | |
| const saved = localStorage.getItem('ot-theme'); | |
| return saved || 'dark'; | |
| }); | |
| useEffect(() => { | |
| document.documentElement.setAttribute('data-theme', theme); | |
| localStorage.setItem('ot-theme', theme); | |
| }, [theme]); | |
| const toggleTheme = useCallback(() => { | |
| // 1. Kill ALL transitions so every element swaps color instantly | |
| document.documentElement.classList.add('theme-switching'); | |
| // 2. Swap theme immediately | |
| setTheme(prev => (prev === 'dark' ? 'light' : 'dark')); | |
| // 3. Force reflow so the new colors paint before transitions re-enable | |
| // eslint-disable-next-line no-unused-expressions | |
| document.documentElement.offsetHeight; | |
| // 4. Re-enable transitions after a single frame | |
| requestAnimationFrame(() => { | |
| requestAnimationFrame(() => { | |
| document.documentElement.classList.remove('theme-switching'); | |
| }); | |
| }); | |
| }, []); | |
| return ( | |
| <ThemeContext.Provider value={{ theme, toggleTheme }}> | |
| {children} | |
| </ThemeContext.Provider> | |
| ); | |
| } | |
| export function useTheme() { | |
| const ctx = useContext(ThemeContext); | |
| if (!ctx) throw new Error('useTheme must be used within ThemeProvider'); | |
| return ctx; | |
| } | |