Skip to content
Clone
main
56 lines 1.5 KB
import { useSyncExternalStore } from 'react'
export type Theme = 'light' | 'dark'
const STORAGE_KEY = 'kernel-theme'
const listeners = new Set<() => void>()
function readInitial(): Theme {
const fromUrl = new URLSearchParams(window.location.search).get('theme')
if (fromUrl === 'light' || fromUrl === 'dark') return fromUrl
const stored = localStorage.getItem(STORAGE_KEY)
if (stored === 'light' || stored === 'dark') return stored
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
let current: Theme = 'light'
function apply() {
document.documentElement.classList.toggle('dark', current === 'dark')
}
/** Call once at app bootstrap, before first paint of themed UI. */
export function initTheme() {
current = readInitial()
apply()
}
export function getTheme(): Theme {
return current
}
export function setTheme(theme: Theme) {
current = theme
localStorage.setItem(STORAGE_KEY, theme)
apply()
for (const listener of listeners) listener()
}
export function toggleTheme() {
setTheme(current === 'dark' ? 'light' : 'dark')
}
/** Subscribe to light/dark changes; the palette store repaints on these. */
export function onThemeChange(listener: () => void): () => void {
listeners.add(listener)
return () => listeners.delete(listener)
}
function subscribe(listener: () => void) {
listeners.add(listener)
return () => listeners.delete(listener)
}
export function useTheme(): Theme {
return useSyncExternalStore(subscribe, getTheme)
}