/ frontend / src / store / theme.ts
theme.ts
 1  import { create } from 'zustand'
 2  import { persist } from 'zustand/middleware'
 3  
 4  type Theme = 'light' | 'dark' | 'system'
 5  
 6  interface ThemeState {
 7    theme: Theme
 8    resolvedTheme: 'light' | 'dark'
 9    setTheme: (theme: Theme) => void
10  }
11  
12  function getSystemTheme(): 'light' | 'dark' {
13    if (typeof window === 'undefined') return 'light'
14    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
15  }
16  
17  function resolveTheme(theme: Theme): 'light' | 'dark' {
18    if (theme === 'system') {
19      return getSystemTheme()
20    }
21    return theme
22  }
23  
24  export const useThemeStore = create<ThemeState>()(
25    persist(
26      (set) => ({
27        theme: 'system',
28        resolvedTheme: resolveTheme('system'),
29        setTheme: (theme: Theme) => {
30          const resolvedTheme = resolveTheme(theme)
31          set({ theme, resolvedTheme })
32  
33          // Apply to document
34          if (resolvedTheme === 'dark') {
35            document.documentElement.classList.add('dark')
36          } else {
37            document.documentElement.classList.remove('dark')
38          }
39        },
40      }),
41      {
42        name: 'acdc-theme',
43        onRehydrateStorage: () => (state) => {
44          // Apply theme on load
45          if (state) {
46            const resolvedTheme = resolveTheme(state.theme)
47            if (resolvedTheme === 'dark') {
48              document.documentElement.classList.add('dark')
49            } else {
50              document.documentElement.classList.remove('dark')
51            }
52          }
53        },
54      }
55    )
56  )
57  
58  // Listen for system theme changes
59  if (typeof window !== 'undefined') {
60    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
61      const state = useThemeStore.getState()
62      if (state.theme === 'system') {
63        const newResolvedTheme = e.matches ? 'dark' : 'light'
64        useThemeStore.setState({ resolvedTheme: newResolvedTheme })
65        if (newResolvedTheme === 'dark') {
66          document.documentElement.classList.add('dark')
67        } else {
68          document.documentElement.classList.remove('dark')
69        }
70      }
71    })
72  }