aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/theme.svelte.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/theme.svelte.ts')
-rw-r--r--src/lib/theme.svelte.ts49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/lib/theme.svelte.ts b/src/lib/theme.svelte.ts
new file mode 100644
index 0000000..956738b
--- /dev/null
+++ b/src/lib/theme.svelte.ts
@@ -0,0 +1,49 @@
+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, Theme> = {
+ [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;