Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion web/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

/* ---- Light Theme (default) ---- */
:root, [data-theme="light"] {
color-scheme: light;
--bg-primary: #fdfbff;
--bg-secondary: #f9f4fb;
--bg-tertiary: #f2e9f4;
Expand Down Expand Up @@ -74,10 +75,19 @@
--section-alt-bg: var(--bg-secondary);

--grid-color: rgba(148, 163, 184, 0.15);

--hl-comment: #64748b;
--hl-arg: #15803d;
--hl-operator:#6b7280;
--hl-value: #a16207;
--hl-keyword: #7c3aed;
--hl-string: #c2410c;
--hl-builtin: #0369a1;
}

/* ---- Dark Theme ---- */
[data-theme="dark"] {
color-scheme: dark;
--bg-primary: #0f1117;
--bg-secondary: #151821;
--bg-tertiary: #1a1d28;
Expand Down Expand Up @@ -115,6 +125,14 @@
--section-alt-bg: var(--bg-secondary);

--grid-color: rgba(31, 41, 55, 0.25);

--hl-comment: #94a3b8;
--hl-arg: #22c55e;
--hl-operator:#e5e7eb;
--hl-value: #eab308;
--hl-keyword: #a855f7;
--hl-string: #f97316;
--hl-builtin: #0ea5e9;
}

/* ---- Reset & Base ---- */
Expand All @@ -137,7 +155,7 @@ body {
background-color: var(--bg-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition: background-color var(--transition-base), color var(--transition-base);
transition: color var(--transition-base);
position: relative;
overflow-x: hidden;
max-width: 100vw;
Expand Down Expand Up @@ -242,6 +260,19 @@ p {
color: white;
}

/* ---- Docs syntax highlighting ---- */
/* Colors are CSS variables so Safari picks them up via inheritance, not
selector re-evaluation (which WebKit defers for complex layouts). */
.hl-comment { color: var(--hl-comment); }
.hl-command { color: var(--accent); }
.hl-arg,
.hl-flag { color: var(--hl-arg); }
.hl-operator { color: var(--hl-operator); }
.hl-value { color: var(--hl-value); }
.hl-keyword { color: var(--hl-keyword); }
.hl-string { color: var(--hl-string); }
.hl-builtin { color: var(--hl-builtin); }

/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
Expand Down
28 changes: 26 additions & 2 deletions web/src/lib/theme.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,32 @@ import { browser } from '$app/environment';

type Theme = 'light' | 'dark';

// Must match --bg-primary in app.css for each theme.
// Safari doesn't repaint body.backgroundColor when CSS vars change on html,
// so we set it directly in JS with the known values.
const BG: Record<Theme, string> = {
light: '#fdfbff',
dark: '#0f1117',
};

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';
}

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());
Expand All @@ -18,12 +36,18 @@ let resolved = $derived<Theme>(preference === 'system' ? getSystemTheme() : pref
function apply(t: Theme) {
if (!browser) return;
document.documentElement.setAttribute('data-theme', t);
document.documentElement.style.colorScheme = t;
// Safari bug: body's background-color using var(--bg-primary) is not repainted
// when the CSS variable changes via a [data-theme] attribute selector on <html>.
// The CSS transition on background-color also fights inline style updates in
// Safari, so we removed that transition and set the value directly here.
document.body.style.backgroundColor = BG[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);
}
Expand Down
Loading