56 lines
1.3 KiB
TypeScript
56 lines
1.3 KiB
TypeScript
import { create } from 'zustand'
|
|
import { persist } from 'zustand/middleware'
|
|
import { config } from '@lib/config'
|
|
import type { Theme } from '@lib/config'
|
|
|
|
interface ThemeState {
|
|
theme: Theme
|
|
setTheme: (theme: Theme) => void
|
|
}
|
|
|
|
const getSystemTheme = (): 'light' | 'dark' => {
|
|
if (typeof window === 'undefined') return 'light'
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
? 'dark'
|
|
: 'light'
|
|
}
|
|
|
|
const applyTheme = (theme: Theme) => {
|
|
const root = document.documentElement
|
|
const resolvedTheme = theme === 'system' ? getSystemTheme() : theme
|
|
|
|
root.classList.remove('light', 'dark')
|
|
root.classList.add(resolvedTheme)
|
|
}
|
|
|
|
export const useThemeStore = create<ThemeState>()(
|
|
persist(
|
|
(set) => ({
|
|
theme: config.defaultTheme,
|
|
setTheme: (theme) => {
|
|
applyTheme(theme)
|
|
set({ theme })
|
|
},
|
|
}),
|
|
{
|
|
name: 'theme-storage',
|
|
onRehydrateStorage: () => (state) => {
|
|
if (state) {
|
|
applyTheme(state.theme)
|
|
}
|
|
},
|
|
},
|
|
),
|
|
)
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window
|
|
.matchMedia('(prefers-color-scheme: dark)')
|
|
.addEventListener('change', () => {
|
|
const { theme } = useThemeStore.getState()
|
|
if (theme === 'system') {
|
|
applyTheme('system')
|
|
}
|
|
})
|
|
}
|