import { onDestroy, onMount } from 'svelte'; import { themeStorage } from './storage'; import { page } from '$app/state'; export enum Theme { Light = 'light', Dark = 'dark', } const oppositeThemeMappings: Record = { [Theme.Light]: Theme.Dark, [Theme.Dark]: Theme.Light }; export class CTheme { public rawTheme = $state(null as null | Theme); public theme = $derived(this.rawTheme ?? Theme.Dark); public opposite = $derived(oppositeThemeMappings[this.theme]); /** * Marks the page a component renders on as fully compatible/tested with theming. * * Call during component init. */ public themeCompatible() { onMount(() => { const t = page.url.searchParams.get('theme') ?? themeStorage.getItem('theme'); if (theme.isThemeValid(t)) theme.set(t); }); $effect(() => { document.documentElement.setAttribute('data-blog-theme', theme.theme); page.url.searchParams.delete('theme'); }); onDestroy(() => { if (typeof document !== 'undefined') document.documentElement.removeAttribute('data-blog-theme'); }); } public isThemeValid(theme: string | null): theme is Theme { return Object.values(Theme).includes(theme as unknown as Theme); } public set(theme: Theme) { themeStorage.setItem('theme', theme); this.theme = theme; } public flip() { this.set(this.opposite); } }; export const theme = new CTheme(); export default theme;