aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2026-01-28 01:06:31 +0100
committerLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2026-01-28 01:06:31 +0100
commit081c2bffb329182d92cf0534dd42b11826a87c39 (patch)
treef5afb6cc13d3520fb5ca48eb5a6c3f5dc5029d8a
parent8a2913dbe233f1604016233f6a5807f28dc9f81d (diff)
downloadmem-estrogen-zone-081c2bffb329182d92cf0534dd42b11826a87c39.tar.gz
mem-estrogen-zone-081c2bffb329182d92cf0534dd42b11826a87c39.tar.bz2
mem-estrogen-zone-081c2bffb329182d92cf0534dd42b11826a87c39.tar.lz
mem-estrogen-zone-081c2bffb329182d92cf0534dd42b11826a87c39.zip

feat: more theme work

-rw-r--r--src/app.css10
-rw-r--r--src/lib/blog/Post.svelte16
-rw-r--r--src/lib/storage.ts1
-rw-r--r--src/lib/theme.svelte.ts49
-rw-r--r--src/routes/blog/+layout.svelte21
5 files changed, 75 insertions, 22 deletions
diff --git a/src/app.css b/src/app.css
index c41d249..c522036 100644
--- a/src/app.css
+++ b/src/app.css
@@ -220,7 +220,7 @@
}
/*
- A Blog Sepia, very TODO
+ TODO: Sepia properly
*/
[data-blog-theme=sepia] {
@apply bg-[#2b261e];
@@ -228,6 +228,14 @@
[data-blog-theme=light] {
@apply invert hue-rotate-180;
}
+[data-blog-theme] {
+ transition-property: filter;
+ transition-timing-function: ease-in-out;
+ transition-duration: 0.4s;
+}
+:root:not([data-blog-theme]) .theme-selector {
+ display: none;
+}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
diff --git a/src/lib/blog/Post.svelte b/src/lib/blog/Post.svelte
index 9fcc319..05afa78 100644
--- a/src/lib/blog/Post.svelte
+++ b/src/lib/blog/Post.svelte
@@ -5,6 +5,7 @@
<script lang="ts">
import { building, dev } from '$app/environment';
import { page } from '$app/state';
+ import theme from '../theme.svelte';
import { parsePostMetadata, type Post } from './Post';
@@ -43,9 +44,8 @@
</p>
{#if !ignorePublishedStatus && dev}
<p class="mt-1 5">
- <a
- href={new URL('?ignore-unpublished=+', page.url).href}
- class="quicklink">Ignore and read anyway</a
+ <a href="?ignore-unpublished" class="quicklink"
+ >Ignore and read anyway</a
>
</p>
{:else if ignorePublishedStatus}
@@ -73,7 +73,15 @@
class="quicklink"
target="_blank"
rel="noopener noreferrer">src</a
- >{/if}<span class="select-none">▒</span>ctime: {meta.created
+ >{/if}<span class="select-none">▒</span><a
+ href="?theme={theme.opposite}"
+ class="quicklink theme-selector"
+ onclick={(e) => {
+ theme.flip();
+ e.currentTarget.blur();
+ e.preventDefault();
+ }}>toggle theme</a
+ ><span class="select-none theme-selector">▒</span>ctime: {meta.created
.toISOString()
.split('T')[0]}<span class="select-none">▒</span>mtime: {meta.updated
.toISOString()
diff --git a/src/lib/storage.ts b/src/lib/storage.ts
index 0a5334d..48eef3c 100644
--- a/src/lib/storage.ts
+++ b/src/lib/storage.ts
@@ -5,3 +5,4 @@ export const sessionStorage = new StorageManager('session', 'mem.estrogen.zone:'
export const memoryStorage = new StorageManager('memory', 'mem.estrogen.zone:');
export const blogStorage = new StorageManager(localStorage, 'blog:');
+export const themeStorage = new StorageManager(localStorage, 'theme:');
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;
diff --git a/src/routes/blog/+layout.svelte b/src/routes/blog/+layout.svelte
index 61ab5f3..073d5c3 100644
--- a/src/routes/blog/+layout.svelte
+++ b/src/routes/blog/+layout.svelte
@@ -1,27 +1,14 @@
<script lang="ts">
- import { blogStorage } from '$/lib/storage';
- import { page } from '$app/state';
- import { onDestroy, onMount, type Snippet } from 'svelte';
+ import theme from '$/lib/theme.svelte';
+ import { type Snippet } from 'svelte';
+
+ theme.themeCompatible();
const {
children,
}: {
children: Snippet;
} = $props();
-
- onMount(() => {
- const theme =
- page.url.searchParams.get('theme') ?? blogStorage.getItem('theme');
- if (theme) {
- blogStorage.setItem('theme', theme);
- document.documentElement.setAttribute('data-blog-theme', theme);
- page.url.searchParams.delete('theme');
- }
- });
- onDestroy(() => {
- if (typeof document !== 'undefined')
- document.documentElement.removeAttribute('data-blog-theme');
- });
</script>
{@render children()}