From a211d9d4a6285f1ee3265352c1b9463768173193 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 19 Jun 2026 16:36:54 +0000 Subject: [PATCH] perf(boot): instant HTML splash in the shell for a faster perceived first paint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The app is a pure client-rendered SPA (ssr=false, adapter-static), so until the JS bundle downloads, parses and mounts, the page is blank — only then does the layout's own loading state appear. True route prerendering would mean turning on SSR, an architectural change at odds with the SPA design. Instead, bake a tiny boot splash into the shell (`app.html`): a centered spinner that paints the moment the HTML is parsed — before the app bundle or its CSS load — covering the blank gap. The root layout removes `#app-splash` the instant it mounts (before `session.load`), so public routes like /login (which render without waiting for the session) appear immediately and protected routes hand off to their own loading UI. `light-dark()` plus early `color-scheme` rules make the splash match the resolved theme (incl. the saved override), so there's no colour flash when the app CSS arrives; it respects `prefers-reduced-motion`. Pure HTML/CSS in the shell — no new requests, no JS framework on the critical path, ~0.6 KB in index.html. Verified on the real static-dist build in headless Chromium: the shell ships `#app-splash`, the layout removes it after mount, the app renders, and there are no runtime errors. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6 --- frontend/src/app.html | 44 ++++++++++++++++++++++++++++++ frontend/src/routes/+layout.svelte | 5 ++++ 2 files changed, 49 insertions(+) diff --git a/frontend/src/app.html b/frontend/src/app.html index 4d9426c2..eb5e0e1f 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -23,9 +23,53 @@ } })(); + + %sveltekit.head% +
+
+
%sveltekit.body%
diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 7f037227..68c3b8c5 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -21,6 +21,11 @@ let ready = $state(false); onMount(async () => { + // The instant HTML boot splash has done its job — the app is mounted, so + // the route (login renders immediately; protected routes show their own + // loading state) is already in the DOM behind it. + document.getElementById('app-splash')?.remove(); + // Redirect old `#/...` bookmarks to the new path before anything else. if (typeof location !== 'undefined' && location.hash.startsWith('#/')) { const mapped = hashUrlToPath(location.hash);