From 55aa8933af492cc9f328136ab8ee5677b919cbb3 Mon Sep 17 00:00:00 2001 From: Senko-san Date: Tue, 9 Jun 2026 13:18:29 +0300 Subject: [PATCH] fix(theme): kill flash of white on dark-themed load The app painted white until mounted and set data-theme, then snapped to the dark theme. Two fixes: - Inline head script (rsbuild html.tags) sets data-theme before first paint, mirroring modern-sk's exact logic (localStorage 'modern-sk-theme' || 'dark') so there's no second flip when the provider mounts. Inline = zero round-trips. - body now paints var(--color-bg) so the themed background shows before React mounts #root and layers the felt grain on top. Co-Authored-By: Claude Opus 4.8 --- rsbuild.config.ts | 12 ++++++++++++ src/styles/global.css | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 96aa071..ea3d97a 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -36,6 +36,18 @@ export default defineConfig({ // "Install app". The service worker (audio offline cache) is registered // from src/index.tsx, not here. tags: [ + // Theme bootstrap — runs inline before first paint to kill the flash of + // white on a dark-themed load. Mirrors modern-sk's own logic exactly + // (localStorage 'modern-sk-theme' || 'dark' → data-theme on ), so + // there's no second flip when mounts. Inline (not an + // external file) so it costs zero round-trips. + { + tag: 'script', + children: + "(function(){try{var t=localStorage.getItem('modern-sk-theme')||'dark';document.documentElement.setAttribute('data-theme',t);}catch(e){}})();", + head: true, + append: false, + }, // Runtime operator config. A classic (non-deferred) head script, so it // runs before the deferred app bundle and window.__APP_CONFIG__ is set by // the time src/config/env.ts reads it. Served from public/ in dev and diff --git a/src/styles/global.css b/src/styles/global.css index 6d70b12..3befac2 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -28,6 +28,11 @@ body { margin: 0; font-family: var(--font-sans); color: var(--fg-1); + /* Paint the themed background immediately. The inline theme script in + index.html (see rsbuild.config.ts) sets [data-theme] before first paint, so + --color-bg resolves to the right value here before React mounts #root and + layers the .modern-sk-felt grain on top — no flash of white. */ + background: var(--color-bg); -webkit-font-smoothing: antialiased; }