From f81a8728e67b313ac43f55c94fb635abf9beca05 Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Fri, 1 May 2026 23:53:55 +0200 Subject: [PATCH] fix(theme): pre-React bootstrap to kill white-flash on dark galleries (#358) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opening a gallery with a dark theme briefly painted a white background between the initial HTML render and React applying the per-event theme. The HTML shipped with no theme info, so the first paint used the default (#fafafa) before /gallery/:slug/info resolved. Two-part fix. 1. Inline bootstrap script in index.html runs synchronously before React mounts. Reads the URL, looks up a per-slug background colour from localStorage (gallery-theme-bg-), and applies it to documentElement immediately. Falls back to #171717 when no cache exists and the OS prefers dark, so first visits with dark OS still land on a dark background. 2. ThemeContext.applyTheme writes the resolved background to localStorage keyed by slug whenever a gallery theme loads. Revisits then hit the bootstrap cache and never see a flash. Added a 200ms transition on html.background-color so the rare cache→API drift (e.g. theme palette changed admin-side since last visit) is a smooth fade instead of a snap. Limitation: first visit on a light-OS device to a dark gallery still flashes once. Killing that case requires a server-rendered theme hint, out of scope for an SPA bootstrap fix. The empty-skeleton-grid part of the same report is already addressed by the 300ms lazy render in #352 — Rekoo-PS just needs to update from v3.32.1-beta.0 to v3.32.2-beta.0+. --- frontend/index.html | 37 ++++++++++++++++++++++++++ frontend/src/contexts/ThemeContext.tsx | 15 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/frontend/index.html b/frontend/index.html index 620c64c2..e3a61e73 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,43 @@ PicPeak - Photo Sharing Platform + +
diff --git a/frontend/src/contexts/ThemeContext.tsx b/frontend/src/contexts/ThemeContext.tsx index a81fc6b6..72dea3ce 100644 --- a/frontend/src/contexts/ThemeContext.tsx +++ b/frontend/src/contexts/ThemeContext.tsx @@ -62,6 +62,21 @@ export const ThemeProvider: React.FC = ({ if (themeConfig.backgroundColor) { root.style.setProperty('--color-background', themeConfig.backgroundColor); + + // Cache the resolved background by slug so the next visit can + // apply it from the inline bootstrap in index.html before React + // mounts (#358 — eliminates the white flash on dark-theme galleries). + try { + const m = window.location.pathname.match(/\/gallery\/([^/?#]+)/); + if (m && m[1]) { + localStorage.setItem( + `gallery-theme-bg-${decodeURIComponent(m[1])}`, + themeConfig.backgroundColor + ); + } + } catch { + /* ignore — caching is best-effort */ + } } if (themeConfig.textColor) {