fix(theme): pre-React bootstrap to kill white-flash on dark galleries (#358)

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-<slug>), 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+.
This commit is contained in:
Paul Nothaft
2026-05-01 23:53:55 +02:00
parent 9597333ddc
commit f81a8728e6
2 changed files with 52 additions and 0 deletions
+15
View File
@@ -62,6 +62,21 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
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) {