A frontend for your PDS
1<script lang="ts">
2 import { onMount } from 'svelte';
3 import { isDark } from './theme';
4
5 // onMount: initialise the store value
6 onMount(() => {
7 const saved = localStorage.getItem('darkMode');
8 if (saved !== null) {
9 isDark.set(saved === 'true');
10 } else {
11 isDark.set(window.matchMedia('(prefers-color-scheme: dark)').matches);
12 }
13 applyTheme();
14 });
15
16 function applyTheme() {
17 $isDark
18 ? document.documentElement.classList.add('dark')
19 : document.documentElement.classList.remove('dark');
20 }
21
22 function toggleDarkMode() {
23 isDark.update(v => {
24 const next = !v;
25 localStorage.setItem('darkMode', next.toString());
26 return next;
27 });
28 applyTheme();
29 }
30</script>
31
32<button
33 type="button"
34 class="dark-mode-toggle"
35 onclick={toggleDarkMode}
36 aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
37 title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
38>
39 {#if isDark}
40 <!-- Sun icon for light mode -->
41 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
42 <circle cx="12" cy="12" r="5"></circle>
43 <line x1="12" y1="1" x2="12" y2="3"></line>
44 <line x1="12" y1="21" x2="12" y2="23"></line>
45 <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
46 <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
47 <line x1="1" y1="12" x2="3" y2="12"></line>
48 <line x1="21" y1="12" x2="23" y2="12"></line>
49 <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
50 <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
51 </svg>
52 {:else}
53 <!-- Moon icon for dark mode -->
54 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
55 <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
56 </svg>
57 {/if}
58</button>
59
60<style>
61 .dark-mode-toggle {
62 position: fixed;
63 bottom: 24px;
64 right: 24px;
65 width: 48px;
66 height: 48px;
67 border-radius: 50%;
68 border: 1px solid var(--border-color);
69 background-color: var(--content-background-color);
70 color: var(--text-color);
71 cursor: pointer;
72 display: flex;
73 align-items: center;
74 justify-content: center;
75 box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
76 transition: all 0.2s ease;
77 z-index: 100;
78 }
79
80 .dark-mode-toggle:hover {
81 transform: scale(1.1);
82 box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
83 }
84
85 .dark-mode-toggle:focus-visible {
86 outline: 2px solid var(--link-color);
87 outline-offset: 2px;
88 }
89
90 @media screen and (max-width: 768px) {
91 .dark-mode-toggle {
92 bottom: 16px;
93 right: 16px;
94 width: 44px;
95 height: 44px;
96 }
97 }
98</style>