You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
1.5 KiB
50 lines
1.5 KiB
import { useEffect, useState } from 'react';
|
|
|
|
export type Theme = 'light' | 'dark' | 'system';
|
|
const STORAGE_KEY = 'baskbuddy-theme';
|
|
|
|
function systemPrefersDark(): boolean {
|
|
return (
|
|
typeof window !== 'undefined' &&
|
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
);
|
|
}
|
|
|
|
export function resolveTheme(theme: Theme): 'light' | 'dark' {
|
|
if (theme === 'system') return systemPrefersDark() ? 'dark' : 'light';
|
|
return theme;
|
|
}
|
|
|
|
export function applyTheme(theme: Theme) {
|
|
if (typeof document === 'undefined') return;
|
|
const resolved = resolveTheme(theme);
|
|
const root = document.documentElement;
|
|
root.setAttribute('data-theme', resolved);
|
|
root.style.colorScheme = resolved;
|
|
}
|
|
|
|
export function getStoredTheme(): Theme {
|
|
if (typeof window === 'undefined') return 'system';
|
|
const stored = localStorage.getItem(STORAGE_KEY) as Theme | null;
|
|
return stored ?? 'system';
|
|
}
|
|
|
|
// Three-state theme: light | dark | system. Persists to localStorage and
|
|
// live-reacts to OS changes while in `system` mode.
|
|
export function useTheme() {
|
|
const [theme, setTheme] = useState<Theme>(() => getStoredTheme());
|
|
|
|
useEffect(() => {
|
|
applyTheme(theme);
|
|
localStorage.setItem(STORAGE_KEY, theme);
|
|
|
|
if (theme === 'system') {
|
|
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
const onChange = () => applyTheme('system');
|
|
mq.addEventListener('change', onChange);
|
|
return () => mq.removeEventListener('change', onChange);
|
|
}
|
|
}, [theme]);
|
|
|
|
return { theme, setTheme } as const;
|
|
}
|
|
|