aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/theme/ThemeMode.tsx
blob: cd91d5d001203f50ae4be14afa21082f506d2824 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { useEffect, useMemo, useState, type ReactNode } from 'react'
import { Theme } from '@astryxdesign/core/theme'
import { defaultThemeSlug, themes } from '../astryx-themes'
import {
  ThemeModeContext,
  type ColorMode,
  type ThemeModeValue,
} from './themeModeContext'

const STORAGE_KEY = 'astryx-starter-mode'

function getInitialMode(): ColorMode {
  if (typeof window === 'undefined') return 'light'

  try {
    const stored = window.localStorage.getItem(STORAGE_KEY)
    if (stored === 'light' || stored === 'dark') return stored
  } catch {
    // Storage can be unavailable in private browsing or embedded contexts.
  }

  return window.matchMedia('(prefers-color-scheme: dark)').matches
    ? 'dark'
    : 'light'
}

export function ThemeModeProvider({ children }: { children: ReactNode }) {
  const [mode, setMode] = useState<ColorMode>(getInitialMode)

  useEffect(() => {
    try {
      window.localStorage.setItem(STORAGE_KEY, mode)
    } catch {
      // The theme still switches for this session if persistence is blocked.
    }
  }, [mode])

  const value = useMemo<ThemeModeValue>(
    () => ({
      mode,
      toggleMode: () => {
        setMode((current) => (current === 'light' ? 'dark' : 'light'))
      },
    }),
    [mode],
  )

  return (
    <Theme theme={themes[defaultThemeSlug]} mode={mode}>
      <ThemeModeContext.Provider value={value}>
        {children}
      </ThemeModeContext.Provider>
    </Theme>
  )
}