theme.tsx

 1import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
 2
 3type Theme = 'light' | 'dark'
 4
 5interface ThemeContextValue {
 6  theme: Theme
 7  toggle: () => void
 8}
 9
10const ThemeContext = createContext<ThemeContextValue>({
11  theme: 'light',
12  toggle: () => {},
13})
14
15function getInitialTheme(): Theme {
16  const stored = localStorage.getItem('theme')
17  if (stored === 'light' || stored === 'dark') return stored
18  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
19}
20
21export function ThemeProvider({ children }: { children: ReactNode }) {
22  const [theme, setTheme] = useState<Theme>(getInitialTheme)
23
24  useEffect(() => {
25    document.documentElement.classList.toggle('dark', theme === 'dark')
26    localStorage.setItem('theme', theme)
27  }, [theme])
28
29  function toggle() {
30    setTheme((t) => (t === 'light' ? 'dark' : 'light'))
31  }
32
33  return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>
34}
35
36export function useTheme() {
37  return useContext(ThemeContext)
38}