/ src / hooks / useTheme.ts
useTheme.ts
  1  /**
  2   * ACDC Theme Hook
  3   * Context-aware theme management with user preferences
  4   */
  5  
  6  import { useState, useEffect, useCallback } from 'react'
  7  import { themeDefaults, type ThemeMode, type Chain } from '../tokens'
  8  
  9  // Context defaults
 10  const CONTEXT_DEFAULTS: Record<string, 'light' | 'dark'> = {
 11    // Alpha-centric pages → Light
 12    governance: 'light',
 13    staking: 'light',
 14    proposals: 'light',
 15    validators: 'light',
 16  
 17    // Delta-centric pages → Dark
 18    trading: 'dark',
 19    dex: 'dark',
 20    orderbook: 'dark',
 21    charts: 'dark',
 22    pools: 'dark',
 23  
 24    // Blended → Dark
 25    dashboard: 'dark',
 26    bridge: 'dark',
 27    wallet: 'dark',
 28    settings: 'dark',
 29  }
 30  
 31  interface ThemeSettings {
 32    alphaDefault: ThemeMode
 33    deltaDefault: ThemeMode
 34    globalOverride: 'light' | 'dark' | 'none'
 35  }
 36  
 37  const DEFAULT_SETTINGS: ThemeSettings = {
 38    alphaDefault: 'light',
 39    deltaDefault: 'dark',
 40    globalOverride: 'none',
 41  }
 42  
 43  export function useTheme(context?: string) {
 44    const [theme, setThemeState] = useState<'light' | 'dark'>('dark')
 45    const [settings, setSettings] = useState<ThemeSettings>(DEFAULT_SETTINGS)
 46  
 47    // Load settings from localStorage
 48    useEffect(() => {
 49      const stored = localStorage.getItem('acdc-theme-settings')
 50      if (stored) {
 51        try {
 52          setSettings(JSON.parse(stored))
 53        } catch {
 54          // Ignore parse errors
 55        }
 56      }
 57    }, [])
 58  
 59    // Determine effective theme
 60    useEffect(() => {
 61      const getEffectiveTheme = (): 'light' | 'dark' => {
 62        // Check global override first
 63        if (settings.globalOverride !== 'none') {
 64          return settings.globalOverride
 65        }
 66  
 67        // Check context-specific preference
 68        if (context) {
 69          const contextPref = localStorage.getItem(`acdc-theme-${context}`)
 70          if (contextPref === 'light' || contextPref === 'dark') {
 71            return contextPref
 72          }
 73        }
 74  
 75        // Check chain-specific defaults
 76        if (context) {
 77          if ((themeDefaults.alpha as readonly string[]).includes(context)) {
 78            return resolveThemeMode(settings.alphaDefault)
 79          }
 80          if ((themeDefaults.delta as readonly string[]).includes(context)) {
 81            return resolveThemeMode(settings.deltaDefault)
 82          }
 83        }
 84  
 85        // Fall back to context default or dark
 86        return context ? (CONTEXT_DEFAULTS[context] || 'dark') : 'dark'
 87      }
 88  
 89      setThemeState(getEffectiveTheme())
 90    }, [context, settings])
 91  
 92    // Apply theme to document (both data-theme and class for CSS compatibility)
 93    useEffect(() => {
 94      document.documentElement.setAttribute('data-theme', theme)
 95      // Also toggle .dark class for Tailwind dark: prefix to work
 96      if (theme === 'dark') {
 97        document.documentElement.classList.add('dark')
 98      } else {
 99        document.documentElement.classList.remove('dark')
100      }
101    }, [theme])
102  
103    const setTheme = useCallback((newTheme: 'light' | 'dark') => {
104      setThemeState(newTheme)
105      if (context) {
106        localStorage.setItem(`acdc-theme-${context}`, newTheme)
107      }
108    }, [context])
109  
110    const toggleTheme = useCallback(() => {
111      setTheme(theme === 'dark' ? 'light' : 'dark')
112    }, [theme, setTheme])
113  
114    const updateSettings = useCallback((newSettings: Partial<ThemeSettings>) => {
115      const updated = { ...settings, ...newSettings }
116      setSettings(updated)
117      localStorage.setItem('acdc-theme-settings', JSON.stringify(updated))
118    }, [settings])
119  
120    return {
121      theme,
122      setTheme,
123      toggleTheme,
124      settings,
125      updateSettings,
126      isDark: theme === 'dark',
127      isLight: theme === 'light',
128    }
129  }
130  
131  function resolveThemeMode(mode: ThemeMode): 'light' | 'dark' {
132    if (mode === 'system') {
133      return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
134    }
135    return mode
136  }
137  
138  export function useChain(chain: Chain = 'alpha') {
139    useEffect(() => {
140      document.documentElement.setAttribute('data-chain', chain)
141    }, [chain])
142  
143    return { chain }
144  }