From 36f6582d5f157918abeacc717a7d91266c6793ce Mon Sep 17 00:00:00 2001 From: maskedsyntax Date: Mon, 16 Mar 2026 21:33:36 +0530 Subject: [PATCH] fix: Safari theme switching on docs page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove background-color from body's CSS transition — Safari's transition engine fights inline style updates when the underlying CSS variable changes simultaneously. Body background is now set directly via JS in the theme apply function. Syntax highlight colors moved to CSS custom properties for reliable Safari inheritance. --- web/src/app.css | 33 ++++++++++++++++++++++++++++++++- web/src/lib/theme.svelte.ts | 28 ++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/web/src/app.css b/web/src/app.css index e342abf..f7eafb3 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; @@ -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; @@ -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 ---- */ @@ -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; @@ -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; diff --git a/web/src/lib/theme.svelte.ts b/web/src/lib/theme.svelte.ts index db5ee31..2e6f7d2 100644 --- a/web/src/lib/theme.svelte.ts +++ b/web/src/lib/theme.svelte.ts @@ -2,6 +2,24 @@ 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 = { + 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'; @@ -9,7 +27,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 +36,18 @@ let resolved = $derived(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 . + // 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); }