From 6434256dfb0569ae848410fd5f2b4dd6a7147d8d Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:39:04 +0100 Subject: [PATCH 1/6] Set dynamic theme color for PWA and Android status bar. Added logic to update the theme-color meta tag dynamically based on the active theme. This improves the visual consistency of the application, particularly for PWA and Android users. Default theme color has also been updated in the configuration. --- frontend/index.html | 1 + frontend/src/styles/themes.js | 30 ++++++++++++++++++++++++++++++ frontend/vite.config.js | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/frontend/index.html b/frontend/index.html index 91f2579..380c08c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,6 +3,7 @@ + Cluedo Sheet diff --git a/frontend/src/styles/themes.js b/frontend/src/styles/themes.js index ff01d9c..cb182c6 100644 --- a/frontend/src/styles/themes.js +++ b/frontend/src/styles/themes.js @@ -195,6 +195,32 @@ export const THEMES = { export const DEFAULT_THEME_KEY = "default"; +export function setThemeColorMeta(color) { + try { + const safe = typeof color === "string" ? color.trim() : ""; + if (!safe) return; + + // only allow solid colors (hex, rgb, hsl); ignore urls/gradients/rgba overlays + const looksSolid = + safe.startsWith("#") || + safe.startsWith("rgb(") || + safe.startsWith("hsl(") || + safe.startsWith("oklch("); + + if (!looksSolid) return; + + let meta = document.querySelector('meta[name="theme-color"]'); + if (!meta) { + meta = document.createElement("meta"); + meta.setAttribute("name", "theme-color"); + document.head.appendChild(meta); + } + meta.setAttribute("content", safe); + } catch { + // ignore + } +} + export function applyTheme(themeKey) { const t = THEMES[themeKey] || THEMES[DEFAULT_THEME_KEY]; const root = document.documentElement; @@ -202,6 +228,10 @@ export function applyTheme(themeKey) { for (const [k, v] of Object.entries(t.tokens)) { root.style.setProperty(`--hp-${k}`, v); } + + // ✅ PWA/Android Statusbar dynamisch an Theme anpassen + // Nimmt (falls vorhanden) statusBarColor, sonst pageBg + setThemeColorMeta(t.tokens.statusBarColor || t.tokens.pageBg || "#000000"); } export function themeStorageKey(email) { diff --git a/frontend/vite.config.js b/frontend/vite.config.js index d23be78..2219097 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -20,7 +20,7 @@ export default defineConfig({ scope: "/", display: "standalone", background_color: "#1c140d", - theme_color: "#caa45a", + theme_color: "#000000", icons: [ { src: "/icons/icon-512.png", sizes: "512x512", type: "image/png" } ] From 070057afb3a40a12f25220e54a2a9bbeedadd950 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:41:45 +0100 Subject: [PATCH 2/6] Set and persist theme preference in localStorage Implemented logic to store and retrieve theme preferences using localStorage for both logged-in users and guests. This ensures the selected theme is applied immediately on load, preventing theme flash issues. Adjusted initialization to apply the correct theme at app startup. --- frontend/index.html | 8 +++++++- frontend/src/App.jsx | 9 +++++++++ frontend/src/main.jsx | 12 ++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 380c08c..5e8af46 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,7 +7,13 @@ Cluedo Sheet - + +
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 84b88dd..8d4e48e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -342,6 +342,14 @@ export default function App() { setThemeKey(key); applyTheme(key); + // ✅ sofort für nächsten Start merken (verhindert Flash) + try { + localStorage.setItem(`hpTheme:${(me?.email || "guest").toLowerCase()}`, key); + localStorage.setItem("hpTheme:guest", key); // fallback, falls noch nicht eingeloggt + } catch { + // ignore + } + try { await api("/auth/theme", { method: "PATCH", @@ -351,6 +359,7 @@ export default function App() { // theme locally already applied; ignore backend error } }; + // ===== Stats (always fresh on open) ===== const openStatsModal = async () => { diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 19def02..9bd8180 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,9 +1,17 @@ import React from "react"; -import { createRoot } from "react-dom/client"; +import ReactDOM from "react-dom/client"; import App from "./App.jsx"; +import { applyTheme, DEFAULT_THEME_KEY } from "./styles/themes"; import { registerSW } from "virtual:pwa-register"; -createRoot(document.getElementById("root")).render(); +try { + const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; + applyTheme(key); +} catch { + applyTheme(DEFAULT_THEME_KEY); +} + +ReactDOM.createRoot(document.getElementById("root")).render(); registerSW({ immediate: true }); const updateSW = registerSW({ immediate: true, From 7c4754e50616df0979a5c6bff87b73719f680608 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:44:44 +0100 Subject: [PATCH 3/6] "Enhance loading experience and optimize theme application Added a fallback background and a preload lock for smoother loading transitions. Improved theme application by applying it prior to React rendering and removed theme flash. Adjusted Service Worker registration for better performance and reliability." --- frontend/index.html | 47 ++++++++++++++++++++++++++++++++++++++----- frontend/src/main.jsx | 20 +++++++++++------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 5e8af46..ab00530 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,12 +2,48 @@ - + Cluedo Sheet - - - + + + + + + + + + + - + +
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 9bd8180..7c3d36e 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -4,6 +4,7 @@ import App from "./App.jsx"; import { applyTheme, DEFAULT_THEME_KEY } from "./styles/themes"; import { registerSW } from "virtual:pwa-register"; +// ✅ Theme VOR React setzen (kein Theme-Flash) try { const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; applyTheme(key); @@ -11,12 +12,17 @@ try { applyTheme(DEFAULT_THEME_KEY); } +// ✅ Preload Unlock (nach Theme!) +document.body.classList.remove("preload"); +document.body.classList.add("ready"); + ReactDOM.createRoot(document.getElementById("root")).render(); -registerSW({ immediate: true }); + +// ✅ Service Worker NUR EINMAL registrieren const updateSW = registerSW({ - immediate: true, - onNeedRefresh() { - updateSW(true); // sofort neue Version aktivieren - window.location.reload(); - }, - }); + immediate: true, + onNeedRefresh() { + updateSW(true); + window.location.reload(); + }, +}); From e975d7aa252874232ff5a931f31afe4864d59d13 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:47:13 +0100 Subject: [PATCH 4/6] Set up immediate theme application and manual SW updates Moved theme application logic to occur immediately on app initialization to prevent UI flash. Added a check to wait for all fonts to load before making the app visible and adjusted Service Worker behavior to require manual updates instead of auto-reloading. --- frontend/src/main.jsx | 53 +++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 7c3d36e..0a6689c 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -4,25 +4,38 @@ import App from "./App.jsx"; import { applyTheme, DEFAULT_THEME_KEY } from "./styles/themes"; import { registerSW } from "virtual:pwa-register"; -// ✅ Theme VOR React setzen (kein Theme-Flash) -try { - const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; - applyTheme(key); -} catch { - applyTheme(DEFAULT_THEME_KEY); +async function bootstrap() { + // ✅ Theme sofort setzen + try { + const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; + applyTheme(key); + } catch { + applyTheme(DEFAULT_THEME_KEY); + } + + // ✅ Warten bis ALLE Fonts geladen sind + try { + if (document.fonts && document.fonts.ready) { + await document.fonts.ready; + } + } catch { + // ignore + } + + // ✅ Erst JETZT sichtbar machen + document.body.classList.remove("preload"); + document.body.classList.add("ready"); + + ReactDOM.createRoot(document.getElementById("root")).render(); + + // ✅ Service Worker – KEIN Auto-Reload mehr + registerSW({ + immediate: true, + onNeedRefresh() { + console.info("Neue Version verfügbar – Reload manuell"); + // optional: später Toast „Update verfügbar“ + }, + }); } -// ✅ Preload Unlock (nach Theme!) -document.body.classList.remove("preload"); -document.body.classList.add("ready"); - -ReactDOM.createRoot(document.getElementById("root")).render(); - -// ✅ Service Worker NUR EINMAL registrieren -const updateSW = registerSW({ - immediate: true, - onNeedRefresh() { - updateSW(true); - window.location.reload(); - }, -}); +bootstrap(); From 57cb9a57efe88f2f2946d30617213e5fccb070f2 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:49:25 +0100 Subject: [PATCH 5/6] Replace preload mechanism with splash screen The preload class was replaced by a more user-friendly splash screen design. This change ensures a smoother transition while loading assets and eliminates black background flashes. The splash overlay is automatically hidden and removed after the app is ready, providing a seamless loading experience. --- frontend/index.html | 54 ++++++++++++++++++++++++++++--------------- frontend/src/main.jsx | 26 +++++++++++---------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index ab00530..3ff78dc 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,10 +2,7 @@ - + Cluedo Sheet @@ -17,33 +14,49 @@ rel="stylesheet" /> - + - + - + + +
+
Zauber-Detektiv Notizbogen
+
+
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 0a6689c..58ec495 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -13,27 +13,29 @@ async function bootstrap() { applyTheme(DEFAULT_THEME_KEY); } - // ✅ Warten bis ALLE Fonts geladen sind + // ✅ Fonts abwarten (kein Layout-Jump) try { - if (document.fonts && document.fonts.ready) { + if (document.fonts?.ready) { await document.fonts.ready; } - } catch { - // ignore - } - - // ✅ Erst JETZT sichtbar machen - document.body.classList.remove("preload"); - document.body.classList.add("ready"); + } catch {} + // React rendern ReactDOM.createRoot(document.getElementById("root")).render(); - // ✅ Service Worker – KEIN Auto-Reload mehr + // ✅ Splash sauber ausblenden (KEIN Schwarz) + const splash = document.getElementById("app-splash"); + if (splash) { + requestAnimationFrame(() => splash.classList.add("hide")); + setTimeout(() => splash.remove(), 220); + } + + // ✅ Service Worker ohne Reload-Flash registerSW({ immediate: true, onNeedRefresh() { - console.info("Neue Version verfügbar – Reload manuell"); - // optional: später Toast „Update verfügbar“ + console.info("Neue Version verfügbar"); + // später Toast möglich }, }); } From 62439d2d28da32ba81fa34504494f9746079d042 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 6 Feb 2026 18:52:16 +0100 Subject: [PATCH 6/6] Enhance loading screen with animation and splash improvements Updated the loading screen to include animated gold lines, a loader, and a spark effect. Adjusted splash visibility to ensure a minimum display time of 3 seconds and improved transitions for smoother visuals. Enhanced style and structure for better user experience during app initialization. --- frontend/index.html | 122 +++++++++++++++++++++++++++++++++++++++--- frontend/src/main.jsx | 30 +++++++---- 2 files changed, 133 insertions(+), 19 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 3ff78dc..b924493 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -14,7 +14,6 @@ rel="stylesheet" /> - @@ -66,9 +168,13 @@ - -
-
Zauber-Detektiv Notizbogen
+
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 58ec495..c0a0bc2 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -5,7 +5,7 @@ import { applyTheme, DEFAULT_THEME_KEY } from "./styles/themes"; import { registerSW } from "virtual:pwa-register"; async function bootstrap() { - // ✅ Theme sofort setzen + // Theme sofort setzen try { const key = localStorage.getItem("hpTheme:guest") || DEFAULT_THEME_KEY; applyTheme(key); @@ -13,29 +13,37 @@ async function bootstrap() { applyTheme(DEFAULT_THEME_KEY); } - // ✅ Fonts abwarten (kein Layout-Jump) + // Fonts abwarten (verhindert Layout-Sprung) try { if (document.fonts?.ready) { await document.fonts.ready; } } catch {} - // React rendern + // App rendern ReactDOM.createRoot(document.getElementById("root")).render(); - // ✅ Splash sauber ausblenden (KEIN Schwarz) - const splash = document.getElementById("app-splash"); - if (splash) { - requestAnimationFrame(() => splash.classList.add("hide")); - setTimeout(() => splash.remove(), 220); - } + // Splash mind. 3 Sekunden anzeigen (3000ms) + const MIN_SPLASH_MS = 3000; + const tStart = performance.now(); - // ✅ Service Worker ohne Reload-Flash + const hideSplash = () => { + const splash = document.getElementById("app-splash"); + if (!splash) return; + splash.classList.add("hide"); + setTimeout(() => splash.remove(), 320); + }; + + const elapsed = performance.now() - tStart; + const remaining = Math.max(0, MIN_SPLASH_MS - elapsed); + setTimeout(hideSplash, remaining); + + // Service Worker ohne Auto-Reload-Flash registerSW({ immediate: true, onNeedRefresh() { console.info("Neue Version verfügbar"); - // später Toast möglich + // optional: später Toast "Update verfügbar" }, }); }