feat(pwa): blue splash screen with the logo, status bar follows the theme

Manifest: short name, blue background/theme colour for the Android splash.
index.html shows a plain blue screen with the logo until React mounts.
theme-color starts blue and then follows the active theme background.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 07:13:29 +01:00
1 parent a3a709440f
commit 9bc8716f41
4 files changed
+32 -6

No files matched your search

+7
View File
@@ -61,3 +61,10 @@ Komponente: [CountBadge.tsx](../frontend/src/components/CountBadge.tsx), CSS-Kla
- `inline`: im Textfluss. - `inline`: im Textfluss.
- Zahlen im mobilen Menü (`MobileActionContext`, `badge: number`) werden automatisch als `CountBadge` gezeigt; Text-Badges wie „2 aktiv“ bleiben Textmarken. - Zahlen im mobilen Menü (`MobileActionContext`, `badge: number`) werden automatisch als `CountBadge` gezeigt; Text-Badges wie „2 aktiv“ bleiben Textmarken.
- **Wichtig für neue Klassen:** Tailwind entfernt Klassen aus `@layer components`, die nicht wörtlich im Code stehen. Klassennamen deshalb nie zusammensetzen (`count-badge-${variant}`), sondern ausschreiben. - **Wichtig für neue Klassen:** Tailwind entfernt Klassen aus `@layer components`, die nicht wörtlich im Code stehen. Klassennamen deshalb nie zusammensetzen (`count-badge-${variant}`), sondern ausschreiben.
## Ladebildschirm & Statusleiste
- `manifest.json`: `name` „MindSift“ (kurz, der Slogan steht in `description`), `background_color` und `theme_color` `#2563eb` – Android baut daraus den Splash-Screen.
- `index.html` enthält in `#root` einen reinen HTML-Platzhalter (vollflächig Blau, Logo in der Mitte, kein Text). React ersetzt ihn beim ersten Rendern, dazwischen blitzt nichts Weißes auf.
- `meta theme-color` startet blau und wird von [`theme.ts`](../frontend/src/lib/theme.ts) nach dem Start und bei jedem Theme-Wechsel auf `--bg-app` des aktiven Themes gesetzt, damit die Statusleiste zum Header passt.
+9 -2
View File
@@ -3,7 +3,8 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#0b0f19" /> <!-- Blue while loading (matches the splash screen); the app switches it to the theme background -->
<meta name="theme-color" content="#2563eb" />
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="MindSift" /> <meta name="apple-mobile-web-app-title" content="MindSift" />
@@ -49,7 +50,13 @@
</script> </script>
</head> </head>
<body class="bg-[#0b0f19] text-[#e3e3e3] antialiased selection:bg-[#8ab4f8]/30 selection:text-[#8ab4f8]"> <body class="bg-[#0b0f19] text-[#e3e3e3] antialiased selection:bg-[#8ab4f8]/30 selection:text-[#8ab4f8]">
<div id="root"></div> <!-- Shown until React mounts and replaces it: full blue with the logo, no text, no white flash -->
<div id="root">
<div style="position:fixed;inset:0;background:#2563eb;display:flex;align-items:center;justify-content:center">
<!-- the icon's own blue square disappears on blue, so the visible brain is ~55% of this size -->
<img src="/icon.svg" width="128" height="128" alt="" style="display:block" />
</div>
</div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
<script> <script>
if ('caches' in window) { if ('caches' in window) {
+4 -4
View File
@@ -1,13 +1,13 @@
{ {
"id": "/", "id": "/",
"name": "MindSift — Der Kopf frei in drei Sekunden", "name": "MindSift",
"short_name": "MindSift", "short_name": "MindSift",
"description": "Instant Brain-Dump, Tinder-Style Triage, Workspace & Echtzeit-Familienlisten.", "description": "MindSift — Der Kopf frei in drei Sekunden. Instant Brain-Dump, Tinder-Style Triage, Workspace & Echtzeit-Familienlisten.",
"start_url": "/", "start_url": "/",
"scope": "/", "scope": "/",
"display": "standalone", "display": "standalone",
"background_color": "#0b0f19", "background_color": "#2563eb",
"theme_color": "#0b0f19", "theme_color": "#2563eb",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"icons": [ "icons": [
{ {
+12
View File
@@ -132,6 +132,18 @@ function applyToDocument(themeId: ThemeId, customCss: string | null): void {
root.setAttribute('data-theme', attr); root.setAttribute('data-theme', attr);
const light = customCss !== null ? isLightColor(readCssVar(customCss, 'bg-app')) : themeId === 'light'; const light = customCss !== null ? isLightColor(readCssVar(customCss, 'bg-app')) : themeId === 'light';
root.classList.toggle('dark', !light); root.classList.toggle('dark', !light);
syncThemeColorMeta();
}
// The status bar (theme-color) starts blue for the splash screen; once the app runs it follows the
// active theme's background so it blends with the header.
function syncThemeColorMeta(): void {
const meta = document.querySelector('meta[name="theme-color"]');
if (!meta || typeof requestAnimationFrame === 'undefined') return;
requestAnimationFrame(() => {
const bg = getComputedStyle(document.documentElement).getPropertyValue('--bg-app').trim();
if (bg) meta.setAttribute('content', bg);
});
} }
function store(key: string, value: string | null): void { function store(key: string, value: string | null): void {