diff options
Diffstat (limited to 'src/theme')
| -rw-r--r-- | src/theme/ThemeMode.tsx | 55 | ||||
| -rw-r--r-- | src/theme/themeModeContext.ts | 10 | ||||
| -rw-r--r-- | src/theme/useThemeMode.ts | 12 |
3 files changed, 77 insertions, 0 deletions
diff --git a/src/theme/ThemeMode.tsx b/src/theme/ThemeMode.tsx new file mode 100644 index 0000000..cd91d5d --- /dev/null +++ b/src/theme/ThemeMode.tsx @@ -0,0 +1,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> + ) +} diff --git a/src/theme/themeModeContext.ts b/src/theme/themeModeContext.ts new file mode 100644 index 0000000..5bbf8ba --- /dev/null +++ b/src/theme/themeModeContext.ts @@ -0,0 +1,10 @@ +import { createContext } from 'react' + +export type ColorMode = 'light' | 'dark' + +export interface ThemeModeValue { + mode: ColorMode + toggleMode: () => void +} + +export const ThemeModeContext = createContext<ThemeModeValue | null>(null) diff --git a/src/theme/useThemeMode.ts b/src/theme/useThemeMode.ts new file mode 100644 index 0000000..9769684 --- /dev/null +++ b/src/theme/useThemeMode.ts @@ -0,0 +1,12 @@ +import { useContext } from 'react' +import { ThemeModeContext, type ThemeModeValue } from './themeModeContext' + +export function useThemeMode(): ThemeModeValue { + const context = useContext(ThemeModeContext) + + if (context === null) { + throw new Error('useThemeMode must be used inside ThemeModeProvider') + } + + return context +} |
