diff --git a/web/src/app.css b/web/src/app.css
index e342abf..1a2a5f0 100644
--- a/web/src/app.css
+++ b/web/src/app.css
@@ -37,6 +37,7 @@
/* ---- Light Theme (default) ---- */
:root, [data-theme="light"] {
+ color-scheme: light;
--bg-primary: #fdfbff;
--bg-secondary: #f9f4fb;
--bg-tertiary: #f2e9f4;
@@ -78,6 +79,7 @@
/* ---- Dark Theme ---- */
[data-theme="dark"] {
+ color-scheme: dark;
--bg-primary: #0f1117;
--bg-secondary: #151821;
--bg-tertiary: #1a1d28;
@@ -242,6 +244,26 @@ p {
color: white;
}
+/* ---- Docs syntax highlighting ---- */
+.hl-comment { color: #64748b; }
+.hl-command { color: var(--accent); }
+.hl-arg,
+.hl-flag { color: #15803d; }
+.hl-operator { color: #6b7280; }
+.hl-value { color: #a16207; }
+.hl-keyword { color: #7c3aed; }
+.hl-string { color: #c2410c; }
+.hl-builtin { color: #0369a1; }
+
+[data-theme="dark"] .hl-comment { color: #94a3b8; }
+[data-theme="dark"] .hl-arg,
+[data-theme="dark"] .hl-flag { color: #22c55e; }
+[data-theme="dark"] .hl-operator { color: #e5e7eb; }
+[data-theme="dark"] .hl-value { color: #eab308; }
+[data-theme="dark"] .hl-keyword { color: #a855f7; }
+[data-theme="dark"] .hl-string { color: #f97316; }
+[data-theme="dark"] .hl-builtin { color: #0ea5e9; }
+
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
diff --git a/web/src/app.html b/web/src/app.html
index 070ae8f..5a48692 100644
--- a/web/src/app.html
+++ b/web/src/app.html
@@ -13,12 +13,13 @@
/>
%sveltekit.head%
diff --git a/web/src/lib/theme.svelte.ts b/web/src/lib/theme.svelte.ts
index db5ee31..54b5e25 100644
--- a/web/src/lib/theme.svelte.ts
+++ b/web/src/lib/theme.svelte.ts
@@ -2,6 +2,16 @@ import { browser } from '$app/environment';
type Theme = 'light' | 'dark';
+function safeLocalStorage(action: 'get' | 'set', key: string, value?: string): string | null {
+ try {
+ if (action === 'get') return localStorage.getItem(key);
+ localStorage.setItem(key, value!);
+ return null;
+ } catch {
+ return null;
+ }
+}
+
function getSystemTheme(): Theme {
if (!browser) return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
@@ -9,7 +19,7 @@ function getSystemTheme(): Theme {
function getStoredPreference(): 'light' | 'dark' | 'system' {
if (!browser) return 'system';
- return (localStorage.getItem('hashprep-theme') as 'light' | 'dark' | 'system') || 'system';
+ return (safeLocalStorage('get', 'hashprep-theme') as 'light' | 'dark' | 'system') || 'system';
}
let preference = $state<'light' | 'dark' | 'system'>(getStoredPreference());
@@ -18,12 +28,15 @@ let resolved = $derived(preference === 'system' ? getSystemTheme() : pref
function apply(t: Theme) {
if (!browser) return;
document.documentElement.setAttribute('data-theme', t);
+ // Safari needs color-scheme set explicitly to reliably trigger a full
+ // style recalculation when the theme attribute changes on .
+ document.documentElement.style.colorScheme = t;
}
function setPreference(pref: 'light' | 'dark' | 'system') {
preference = pref;
if (browser) {
- localStorage.setItem('hashprep-theme', pref);
+ safeLocalStorage('set', 'hashprep-theme', pref);
}
apply(pref === 'system' ? getSystemTheme() : pref);
}
diff --git a/web/src/routes/docs/+page.svelte b/web/src/routes/docs/+page.svelte
index 13a93a2..72ef1b1 100644
--- a/web/src/routes/docs/+page.svelte
+++ b/web/src/routes/docs/+page.svelte
@@ -716,10 +716,6 @@ summary = analyzer.analyze()
margin-bottom: 20px;
}
- code {
- font-family: var(--font-mono);
- }
-
pre::before {
position: absolute;
inset-block-start: 6px;
@@ -742,49 +738,6 @@ summary = analyzer.analyze()
content: 'Python';
}
- /* Syntax highlighting — light-mode defaults */
- .hl-comment {
- color: #64748b;
- }
-
- .hl-command {
- color: var(--accent);
- }
-
- .hl-arg,
- .hl-flag {
- color: #15803d;
- }
-
- .hl-operator {
- color: #6b7280;
- }
-
- .hl-value {
- color: #a16207;
- }
-
- .hl-keyword {
- color: #7c3aed;
- }
-
- .hl-string {
- color: #c2410c;
- }
-
- .hl-builtin {
- color: #0369a1;
- }
-
- /* Syntax highlighting — dark-mode overrides */
- :global([data-theme="dark"]) .hl-comment { color: #94a3b8; }
- :global([data-theme="dark"]) .hl-arg,
- :global([data-theme="dark"]) .hl-flag { color: #22c55e; }
- :global([data-theme="dark"]) .hl-operator { color: #e5e7eb; }
- :global([data-theme="dark"]) .hl-value { color: #eab308; }
- :global([data-theme="dark"]) .hl-keyword { color: #a855f7; }
- :global([data-theme="dark"]) .hl-string { color: #f97316; }
- :global([data-theme="dark"]) .hl-builtin { color: #0ea5e9; }
@media (max-width: 960px) {
.docs-layout {
diff --git a/web/vercel.json b/web/vercel.json
new file mode 100644
index 0000000..bd6fd8f
--- /dev/null
+++ b/web/vercel.json
@@ -0,0 +1,3 @@
+{
+ "cleanUrls": true
+}