feat: add theme switcher, dark theme

This commit is contained in:
2026-08-25 14:32:13 +05:00
parent 5b22409d41
commit f1f5bd4409
8 changed files with 274 additions and 5 deletions
+41
View File
@@ -0,0 +1,41 @@
export type ThemeChoice = 'light' | 'dark';
const STORAGE_KEY = 'theme';
let theme = $state<ThemeChoice>('light');
function storage(): Storage | null {
return typeof localStorage === 'undefined' ? null : localStorage;
}
function systemTheme(): ThemeChoice {
if (typeof window === 'undefined' || !window.matchMedia) return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function apply(): void {
if (typeof document === 'undefined') return;
document.documentElement.dataset.theme = theme;
}
export function getTheme(): ThemeChoice {
return theme;
}
export function setTheme(next: ThemeChoice): void {
theme = next;
storage()?.setItem(STORAGE_KEY, next);
apply();
}
/**
* Догоняет состояние после гидрации: фактический атрибут уже выставлен
* инлайн-скриптом в app.html; здесь он читается и синхронизируется с runes,
* чтобы кнопка в шапке отражала реальную тему.
*/
export function initTheme(): void {
if (typeof window === 'undefined') return;
const attr = typeof document !== 'undefined' ? document.documentElement.dataset.theme : undefined;
theme = attr === 'dark' || attr === 'light' ? attr : systemTheme();
apply();
}