an atproto based link aggregator
1<script lang="ts">
2 import { browser } from '$app/environment';
3
4 let dark = $state(false);
5
6 // Initialize from localStorage or system preference
7 if (browser) {
8 const stored = localStorage.getItem('theme');
9 if (stored) {
10 dark = stored === 'dark';
11 } else {
12 dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
13 }
14 updateTheme();
15 }
16
17 function updateTheme() {
18 if (dark) {
19 document.documentElement.classList.add('dark');
20 } else {
21 document.documentElement.classList.remove('dark');
22 }
23 }
24
25 function toggle() {
26 dark = !dark;
27 localStorage.setItem('theme', dark ? 'dark' : 'light');
28 updateTheme();
29 }
30</script>
31
32<button
33 onclick={toggle}
34 class="rounded-lg p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
35 aria-label={dark ? 'Switch to light mode' : 'Switch to dark mode'}
36>
37 {#if dark}
38 <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
39 <path
40 stroke-linecap="round"
41 stroke-linejoin="round"
42 stroke-width="2"
43 d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
44 />
45 </svg>
46 {:else}
47 <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
48 <path
49 stroke-linecap="round"
50 stroke-linejoin="round"
51 stroke-width="2"
52 d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
53 />
54 </svg>
55 {/if}
56</button>