Frontend-Audit: Silverscale als iPhone-App
Datum: 07.06.2026 · Stand: v1.7 (nach ADR-40, Capacitor-Hülle live auf Dennis' iPhone) Annahme: Die Capacitor-iOS-App wird der primäre Nutzungsweg – für Dennis und (bald) Jaqueline. Anspruch: Die App soll sich wie eine echte, hochwertige iOS-App anfühlen. Die Nutzerin weiß nicht, dass darunter eine Website läuft – und soll es auch nie merken. Scope: Nur Audit + Plan, kein Code geändert.
Methodik: Code-Review des kompletten Frontends (app/frontend/src, 23 Svelte-Dateien, ~6 200 Zeilen) und der Capacitor-Hülle (app/ios-shell), drei unabhängige Analyse-Durchläufe (Native-Illusion, UX-Flows, Design/A11y), plus frischer Screenshot-Sweep aller 16 Routen (iPhone-Viewport 390×844, hell + dunkel, Chromium). Befunde wurden gegen die Screenshots verifiziert; nicht verifizierbare Punkte sind als (prüfen) markiert. Einschränkung: Der Sweep lief in Chromium, nicht in WKWebView – echte Geräte-Eigenheiten (Keyboard, Rubber-Banding, Swipe-Back) sind aus Code + iOS-Doku abgeleitet.
1. Executive Summary
Silverscale ist als Web-App bemerkenswert gut gebaut: durchdachtes OKLCH-Token-System mit sauberem Dark Mode, Bottom-Sheets mit History-Integration, Safe-Area-Handling, 16-px-Inputs gegen iOS-Zoom, prefers-reduced-motion, Undo-Toasts im Tagebuch, ein cleverer Geräte-Code-Login für die WKWebView. Das Fundament stimmt – hier ist in den letzten Wochen sichtbar viel richtig gemacht worden.
Als „native iOS-App" gemessen, fällt sie aber an den Rändern auf – und genau die Ränder entscheiden über den Wife-Acceptance-Factor:
- Verpackung verrät alles, bevor die App überhaupt offen ist. App-Icon und Splash-Screen sind noch das Default-Capacitor-Logo (blaues X auf weißem Grid). Das ist das Erste, was Jaqueline auf ihrem Homescreen sieht – und der billigste Illusionsbruch der gesamten Liste.
- Die App hängt zu 100 % am Netz – verhält sich aber nicht so. Remote-Modus heißt: jeder Tap ist ein Internet-Roundtrip. Es gibt aber keinen Retry, kein konsistentes Optimistic-UI mit Rollback, keine Lade-Skeletons und beim Offline-Kaltstart eine weiße Wand bzw. einen WKWebView-Fehler. Im Lidl-Funkloch beim Abhaken der Einkaufsliste wird das real.
- Touch-Semantik ist Web, nicht iOS. Long-Press öffnet Textlupe/Kopieren-Menü, Hover-Stile kleben nach dem Tippen, kein Swipe-Back zwischen Seiten, kein haptisches Feedback, Scroll-Physik ohne Bounce. Externe Links (Garmin, OpenFoodFacts) kapern die WKWebView – die App „wird" plötzlich garmin.com, ohne Zurück-Knopf.
- Die App weiß nicht, dass sie eine App ist. Die Einstellungen zeigen in der nativen App die PWA-Anleitung „Teilen → Zum Home-Bildschirm" für Push – ein Text, der in der App falsch und verwirrend ist (und Web-Push geht in der WKWebView ohnehin nicht).
Nichts davon ist ein Architekturproblem. Es ist konzentrierte Randarbeit: Phase 0 („Jaqueline-Gate", ~1 Arbeitstag) macht die App rollout-fähig, zwei weitere Phasen heben sie auf echtes Native-Niveau. Detailplan in §6.
Gesamtnote als Web-App: sehr gut. Als iOS-App heute: befriedigend mit klarem, kurzem Weg zu sehr gut.
2. Zusammenfassung in einfachen Worten
Stell dir vor, Jaqueline bekommt nächste Woche die App aufs iPhone. Was passiert?
- Sie sieht auf dem Homescreen ein fremdes blaues Logo, das nicht nach „unserer App" aussieht. (Das Standard-Logo des Baukastens wurde noch nicht ersetzt.)
- Sie öffnet die App: alles sieht hübsch und app-mäßig aus – gut! Tagebuch, Wochenplan, Gerichte funktionieren und sind verständlich.
- Sie drückt etwas zu lange auf einen Text → es erscheint die Textlupe mit „Kopieren", wie auf einer Webseite. Erster Riss.
- Sie tippt im Fitnessbereich auf den Garmin-Link → plötzlich ist die ganze App eine Garmin-Webseite, ohne Weg zurück außer App beenden.
- Im Supermarkt mit schlechtem Empfang hakt sie Artikel ab → manche Haken springen zurück oder gehen verloren, eine Fehlermeldung mit Zahlencode erscheint.
- In den Einstellungen empfiehlt die App ihr, „die Seite über Teilen zum Home-Bildschirm hinzuzufügen" – welche Seite? Sie ist doch in einer App.
- Und in ca. 7 Tagen startet die App eventuell gar nicht mehr (SideStore-Signatur abgelaufen), ohne jede Erklärung.
Jeder Punkt für sich ist klein. Zusammen entscheiden sie, ob die App „die tolle App von Dennis" oder „die komische Bastel-App" wird. Die gute Nachricht: Das Innere stimmt schon – es geht fast nur um die Hülle, das Verhalten bei schlechtem Netz und ein paar Touch-Details. Das meiste davon ist in einem konzentrierten Tag erledigt.
3. Was bereits richtig gut ist
Damit der kritische Teil die Proportionen nicht verzerrt – diese Dinge sind auf oder über 2026-Niveau:
| Was | Beleg | |
|---|---|---|
| ✅ | Design-Token-System: OKLCH-Palette, semantische Tokens, Spacing-/Radius-Skala, zentrale Schatten | app.css:6–107 |
| ✅ | Dark Mode: eigene Token-Overrides, Theme-fähige theme-color-Meta, im Sweep durchweg lesbar (auch Hero/Ring – ein Agent-Verdacht „weißer Text unsichtbar" hat sich nicht bestätigt) |
app.css:110–144, Screenshots unten |
| ✅ | Sheet-Architektur: Bottom-Sheet mobil / Dialog am Desktop, History-Back schließt das oberste Sheet (Stack-Logik nach Bugfix 07.06.), Escape, role="dialog" |
Sheet.svelte |
| ✅ | iOS-Grundlagen: env(safe-area-inset-*) in Header/Bottom-Nav/Sheets, 16-px-Inputs gegen Auto-Zoom, -webkit-tap-highlight-color, playsinline im Scanner, prefers-reduced-motion |
app.css, ScanSheet.svelte |
| ✅ | Undo-Kultur (angefangen): Tagebuch-Löschen mit „Rückgängig"-Toast, ConfirmDialog statt window.confirm |
Diary.svelte:81–86, stores.js:47–59 |
| ✅ | Geräte-Code-Login (ADR-40): pragmatische, saubere Lösung für das WebAuthn-Problem der WKWebView | backend/auth.py, Settings |
| ✅ | Frische-Logik: Visibility-Refresh für den Wir-Modus, adaptives AI-Polling, Offline-/Online-Toasts | stores.js:65–82, 165–192 |
| ✅ | Typografie: Fraunces als Charakter-Font, Inter Tight als Arbeitsfont, tabular-nums für alle Zahlen |
app.css:53–56, 178 |
Heute-Ansicht (hell). Hero-Ring, Makros, Fitness-Karten – dichte, aber klare Hierarchie.
Dieselbe Ansicht im Dark Mode: Kontraste halten, Token-Overrides greifen.
4. Detailbefunde
Schweregrade: 🔴 kritisch (bricht Illusion oder kostet Daten) · 🟠 mittel (fällt im Alltag auf) · 🟡 klein (Polish).
4.1 Verpackung & Identität der Hülle
F1 🔴 App-Icon und Splash sind das Default-Capacitor-Logo.
ios-shell/ios/App/App/Assets.xcassets/ enthält unverändert AppIcon-512@2x.png und splash-2732x2732.png mit dem Capacitor-X. Das ist der erste und nachhaltigste Eindruck auf dem Homescreen – noch vor jedem Pixel der eigentlichen App. Fix: 1024er-Icon (🐟-Branding, maritimer Verlauf aus --ss-grad-depth) + Splash in --ss-bg mit zentriertem Logo; cap sync, IPA neu bauen (der eine IPA-Rebuild, der sich wirklich lohnt).
Ist-Zustand: Dieses Icon liegt aktuell auf dem Homescreen.
F2 🔴 Offline-/Fehler-Kaltstart = weiße Wand.
Remote-Modus: server.url zeigt auf die Domain, www/index.html ist ein Platzhalter mit einem Satz Text, der Service Worker cacht bewusst nichts (ADR-17). Startet die App ohne Netz (Flugmodus, Keller, Funkloch), zeigt WKWebView eine leere/Fehler-Ansicht ohne Branding und ohne „Nochmal versuchen". Fix-Idee: lokale Fallback-Seite in www/ (Logo, „Keine Verbindung", Retry-Button der auf die Domain redirected) – Capacitor lädt sie zwar nicht automatisch bei Remote-Fehlern, aber ein kleines errorPath-/AppDelegate-Handling bzw. minimaler App-Shell-Cache im SW sind beide gangbar. Entscheidung nötig (→ Offene Frage 3).
F3 🟠 Landscape ist freigeschaltet, aber nicht designt.
Info.plist erlaubt Portrait + beide Landscape-Richtungen. Kein einziges Layout (Bottom-Nav-Höhe, Sheets mit 88dvh, Safe-Area-„Ohren" der Dynamic Island, fehlende safe-area-inset-left/right) ist für Landscape gedacht oder getestet. Empfehlung: Portrait-Lock – eine Zeile Info.plist, eliminiert eine ganze Fehlerklasse. Native Food-Tracker (Yazio, MyFitnessPal) sind ebenfalls portrait-only.
F4 🟠 Statusbar/Start-Übergang unkonfiguriert.
Kein @capacitor/status-bar/splash-screen-Plugin: Die Statusbar-Färbung hängt allein an der Web-theme-color, der Übergang Splash → geladene Remote-Seite ist ein weißer Blitz (auf Mobilfunk auch mal 1–3 s). Fix: Splash-Hintergrund = --ss-bg, StatusBar-Style passend zum Theme, SplashScreen erst nach bootstrap-Antwort ausblenden (SplashScreen.hide() aus dem Frontend, guarded per Capacitor-Detection).
4.2 Touch & Gesten – wo das Web durchschimmert
F5 🔴 Long-Press öffnet Textlupe und Kopieren-Menü.
Nirgendwo -webkit-user-select: none / -webkit-touch-callout: none auf UI-Chrome (Buttons, Chips, Nav, Karten-Titel, Zahlen). Long-Press auf irgendein Label → iOS-Textauswahl mit Lupe. Der zuverlässigste „Das ist ja eine Webseite!"-Moment überhaupt. Fix: global user-select: none auf interaktiven Elementen + gezielt user-select: text dort, wo Kopieren gewollt ist (z. B. Bon-Summen, EANs).
F6 🟠 Hover-Stile kleben auf Touch.
:hover-Regeln (App.svelte:214, Stepper, Listenzeilen, .x:hover u. v. m.) feuern auf iOS beim Tap und bleiben bis zum nächsten Tap stehen. Fix: alle Hover-Stile in @media (hover: hover) einhausen; auf Touch stattdessen :active-Zustände (die .btn:active-Scale existiert schon, gilt aber nicht für Listen/Chips/Nav-Items).
F7 🟠 Kein Swipe-Back.
Seitenwechsel gehen nur über Buttons/Tabs; die iOS-Kerngeste „vom linken Rand zurückwischen" tut nichts (in WKWebView standardmäßig deaktiviert). Da das Routing History-basiert ist (Hash + Sheet-pushState), würde allowsBackForwardNavigationGestures = true im AppDelegate Swipe-Back für Seiten und Sheets quasi gratis aktivieren. Einmaliger IPA-Rebuild (mit F1 bündeln). (Auf Geräte-Verhalten mit dem Sheet-Stack testen.)
F8 🟡 Kein haptisches Feedback.
Eintragen, Abhaken, Löschen, Favorit – alles stumm. Native iOS-Apps quittieren Kernaktionen mit dezenter Haptik. Capacitor-Bridge ist im Remote-Modus verfügbar → @capacitor/haptics (guarded: nur wenn Capacitor.isNativePlatform()), an ~6 Stellen (Essen geloggt, Liste abgehakt, Undo, Scan-Treffer) reicht völlig.
F9 🟡 Scroll-Physik fühlt sich „tot" an.
overscroll-behavior-y: none global (app.css:156) unterbindet das iOS-typische Rubber-Banding. Verständlich als Pull-to-Refresh-Schutz fürs Web – aber in der App ist federloses Scrollen ein subtiler Fremdkörper. Option: Bounce zulassen und kontrolliertes Pull-to-Refresh anbieten (Heute/Tagebuch/Liste wären natürliche Kandidaten, ersetzt zudem den „unsichtbaren" Visibility-Refresh). → Offene Frage 4.
F10 🟡 Touch-Targets teils unter 44 pt.
Profil-Avatare 38 px (App.svelte:189), Sheet-Schließen-✕ ohne Mindestmaß (Sheet.svelte:76), kleine Chips (app.css:206). Die Einkaufslisten-Checkbox ist via ::before-Inset vorbildlich vergrößert – dieses Muster konsequent überall anwenden.
4.3 Netz & Robustheit – Remote-Modus heißt: alles ist Internet
F11 🔴 api.js ohne Retry, Fehlermeldungen technisch.
Ein fetch, ein Wurf: Bei Timeout/5xx/Abbruch erscheint „Fehler: 503" o. ä. (api.js:16–26) – kein automatischer Retry (auch nicht für idempotente GETs), kein freundlicher Text, kein „Nochmal"-Button im Toast. Auf Mobilfunk ist ein verlorenes Paket Normalbetrieb, kein Ausnahmefall. Fix: 1–2 Retrys mit Backoff für GETs; für Schreibzugriffe Fehlertext in Menschensprache + Retry-Action im Toast.
F12 🟠 Optimistic-UI inkonsistent, teils ohne Rollback.
Drei Muster nebeneinander: Tagebuch-Löschen optimistisch mit Undo (gut) · Einkaufslisten-Haken optimistisch ohne Rollback bei Fehler (ShoppingList.svelte:51–56 – Haken bleibt falsch stehen) · Plan-Eintrag-Löschen mit Server-Roundtrip ohne Undo (Plan.svelte:85). Fix: ein Standard-Pattern (optimistisch + Rollback + ggf. Undo) als Helper, überall anwenden.
F13 🟠 Keine Lade-Skeletons, Layout springt.
Seiten-/Profilwechsel rendern erst nach dem API-Roundtrip; bei Mobilfunk-Latenz gibt es leere Karten, springende Ringe und Verschiebungen (Today.svelte:25–30, Plan.svelte:51). Fix: leichte Skeleton-Zustände (Ring-Platzhalter, 3 graue Zeilen) – gefühlte Performance ist auf dem iPhone wichtiger als reale.
F14 🟡 Rückkehr aus dem Hintergrund > 30 min = harter location.reload().
stores.js:75 – gewollt für frische Bundles, aber in der App sieht das nach „App startet neu" aus (weiße Wand auf Mobilfunk). Milder: Reload nur bei tatsächlich neuer Frontend-Version (Build-Hash-Endpoint), sonst Daten-Bump.
F15 🟡 Foto-Logging ohne Bestätigung.
Foto-Schnelleintrag lädt direkt hoch, die KI schätzt und bucht ein (FoodSheet.svelte, Toast „Die KI schätzt …"); beim Barcode-Scan gibt es das Ergebnis-Popup (ADR-24), beim Foto nicht. Falsch erkannte Mahlzeit landet kommentarlos im Tagebuch → Vorschau-/Korrektur-Schritt analog Scan-Popup.
4.4 Wife-Acceptance-Factor: Alltag, Verständlichkeit, Vertrauen
F16 🔴 Externe Links kapern die App.
target="_blank" (Garmin-Connect-Link Diary.svelte:172, OFF-Links Article.svelte:189, Aktivitäts-Links) öffnet in der WKWebView im selben Fenster: Die App „verwandelt sich" in garmin.com – ohne App-Chrome, ohne Zurück. Einzige Rettung: App killen. Fix: zentraler Link-Handler – in der App via window.open-Intercept/@capacitor/browser als In-App-Safari-Sheet (SFSafariViewController, fühlt sich nativ an), im Web weiterhin _blank.
F17 🔴 Einstellungen sprechen Web-Sprache in der App.
Der Push-Hinweis „Seite über Teilen → ‚Zum Home-Bildschirm' installieren" (Settings.svelte:146) erscheint genauso in der Capacitor-App – wo es weder „Teilen" noch eine sinnvolle PWA-Installation gibt und Web-Push prinzipbedingt nicht funktioniert. Für eine Nutzerin, die nicht weiß, dass es eine Website ist, ist dieser Text ein direkter Bruch. Fix: App-Erkennung (UA SilverscaleApp ist schon da!) → in der App diesen Block ersetzen („Mitteilungen kommen aufs gekoppelte Gerät" o. ä.) – und grundsätzlich die Benachrichtigungsstrategie klären (→ Offene Frage 1). Auch der „📱 Geräte-Login"-Block gehört in der App versteckt (man ist ja schon drin).
Einstellungen: Der gelbe Hinweis-Block erklärt PWA-Installation – in der nativen App falsch. Gut dagegen: Profile, Budget und Geräte-Login klar strukturiert.
F18 🟠 Profil-Switcher: leise, folgenreich, nur Emoji.
Zwei Emoji-Avatare im Header (App.svelte:89–100), kein Name, kein Wechsel-Feedback – die gesamte App (Tagebuch! Budget!) wechselt still den Kontext. Klassischer Multi-User-Unfall: versehentlich im falschen Profil tracken und es abends merken. Fix: aktiven Namen anzeigen (oder Toast „Du trackst jetzt als Jaqueline 🐠"), deutlicherer Aktiv-Zustand, evtl. Akzentfarbe pro Profil.
F19 🟠 Sheets verwerfen Eingaben ohne Rückfrage.
Backdrop-Tap, ✕ oder Zurück-Geste schließen Sheets sofort – halb ausgefüllte Portionen/Custom-Einträge sind weg (Sheet.svelte, FoodSheet.svelte). Fix: Dirty-Flag pro Sheet; bei Eingaben ConfirmDialog „Eingaben verwerfen?".
F20 🟠 Onboarding-Lücken für die zweite Nutzerin. Makro-Ziele sind optional und unbeworben (Ring zeigt dann nur kcal, ohne Hinweis warum), leere Tagebuch-Tage zeigen vier stumme „+ eintragen"-Buttons statt einer Einladung, der erste Einstieg (Geräte-Code von Dennis' Gerät holen) braucht Anleitung. Kein Blocker, aber der erste Eindruck entscheidet. Fix: dezente Hinweis-Chips („Makro-Ziele festlegen →"), ein einladender Empty-State im Tagebuch, Mini-Checkliste für den Ersteinrichtungstag.
F21 🔴 (operativ, nicht Frontend) SideStore-Ablauf = App plötzlich tot. Verpasster 7-Tage-Refresh → App startet kommentarlos nicht mehr. Größte einzelne WAF-Bombe überhaupt, weil sie nach gelungener Einführung explodiert. Gehört nicht zum Frontend, muss aber im Plan stehen: 14.06.-Check ernst nehmen, Refresh-Routine für Jaquelines Gerät definieren, ggf. PWA als dokumentierter Fallback („wenn die App nicht startet, nimm das Lesezeichen").
4.5 Design-System, A11y, Konsistenz-Polish
F22 🟠 Zwei Icon-Sprachen in einer Navigation. Haupt-Tabs: feine SVG-Strokes. „Mehr"-Menü + Desktop-Sidebar: bunte Emojis (🫀🛒🪸📦🧾📈⚙️). Der Stilbruch passiert genau beim Öffnen des Mehr-Sheets. Emojis als Inhalts-Sprache (Profile, Mahlzeiten ☀️🌙, Metriken) sind charmant und dürfen bleiben – als Navigations-Icons wirken sie neben den SVGs unentschieden. Fix: 7 SVG-Icons im Stil der Haupt-Tabs nachziehen.
F23 🟠 A11y-Basics mit Lücken.
Gut: focus-visible, aria-label auf Avataren/✕, role="dialog"/alertdialog. Fehlt: Focus-Trap + Focus-Return in Sheets (VoiceOver entkommt in den Hintergrund), aria-live für Toasts und KI-Status (dynamische Änderungen bleiben stumm), Labels auf Favoriten-Sternen (Articles.svelte:93) und einigen Emoji-Buttons. Mit VoiceOver ist die App aktuell nur teilweise bedienbar – für 2026 ein Soll, kein Kann.
F24 🟡 Token-Treue ~90 %, Rest Wildwuchs.
Hartkodierte Farben mit Dark-Mode-Risiko (#3a2c00 auf Amber-Chips in Articles.svelte/Receipt.svelte, NutriChip-Paare, rgba()-Schatten in Activity.svelte:595), Inline-margin-Pixel statt --ss-space-*, @keyframes sonar dreifach kopiert (App/Dish/Stats), .row-Listenmuster fünffach leicht unterschiedlich. Nichts davon sichtbar kaputt – aber jede künftige Theme-Änderung stolpert darüber.
F25 🟡 Seitenwechsel sind harte Schnitte.
Hash-Routing tauscht <main> instantan; zusammen mit Lade-Roundtrips wirken Wechsel abrupt (Web-Feeling). Schon ein 150-ms-Crossfade ({#key $route.page} + fade) plus Skeletons (F13) erzeugt App-Gefühl; echte Push-Transitions wären Kür.
F26 🟡 Statistik-Charts im Sweep leer. (prüfen) Im Screenshot rendert „Ausgaben pro Monat" als leere Fläche (nur Ø-Zeile), auch der kcal-Verlauf bleibt blass. Kann ein Screenshot-Timing-Artefakt sein (Canvas/Animation) – auf dem Gerät verifizieren; falls echt, ist die Statistik-Seite de facto leer.
Statistik im Audit-Sweep: leere Chart-Flächen (prüfen), Top-Produkte-Liste dagegen stark.
Screenshot-Galerie (weitere Kernseiten)
Tagebuch: klare Mahlzeiten-Struktur, Quell-Badges, Undo beim Löschen – die stärkste Seite der App.
Wochenplan: HF-Box-Integration, „einplanen"-Flow. Lange Scroll-Strecke; gegessen-Status gut sichtbar.
Gerichte-Galerie: Foto-Grid mit HF-Kennzeichnung – wertig. Karten-Tap-Flächen großzügig.
Gericht-Detail: Zutaten mit Vorrats-Punkten, Schritt-Anleitung mit Bildern, „Essen loggen"-CTA.
Einkaufsliste: „Aus Wochenplan", Preisschätzung, 1-Klick-Zuletzt-verwendet. Hier zählt Offline-Robustheit (F11/F12) am meisten.
Fitness: eGYM/Garmin-Karten mit Sparklines – dichter, motivierender Screen.
Vorräte: lange gruppierte Liste mit Steppern; Kandidat für klebrige Hover-Stile (F6) und 44-pt-Targets (F10).
5. Top 10 Improvements/Fixes
Priorisiert nach (Illusionsbruch × Alltagshäufigkeit) ÷ Aufwand. „IPA" = erfordert einmaligen Rebuild der Hülle.
| # | Fix | Befunde | Aufwand | Wirkung |
|---|---|---|---|---|
| 1 | Icon + Splash branden, Portrait-Lock, Swipe-Back aktivieren – ein gebündelter IPA-Rebuild (Assets, Info.plist, allowsBackForwardNavigationGestures) |
F1 F3 F7 | ½ Tag (IPA) | Homescreen-Eindruck + Kerngeste + ganze Fehlerklasse weg |
| 2 | Externe Links → In-App-Safari: zentraler Link-Handler, in der App SFSafariViewController-Sheet, im Web _blank |
F16 | 2–3 h | „App-Kaperung" durch Garmin/OFF beseitigt |
| 3 | Touch-Semantik nativieren: user-select/touch-callout aus auf UI-Chrome, Hover hinter @media (hover: hover), :active-Feedback auf Listen/Chips |
F5 F6 | 2–3 h | Der häufigste Illusionsbruch (Long-Press/Klebe-Hover) verschwindet |
| 4 | App-Bewusstsein in Einstellungen: UA SilverscaleApp erkennen → PWA-/Push-Hinweise und Geräte-Login-Block in der App ersetzen/verstecken |
F17 | 1–2 h | Kein Web-Vokabular mehr in der App |
| 5 | Netz-Robustheit: GET-Retry mit Backoff, Schreibfehler in Menschensprache + „Nochmal"-Action, einheitliches Optimistic-Pattern mit Rollback (Liste!), Plan-Löschen mit Undo | F11 F12 | ½ Tag | Funkloch kostet keine Daten und kein Vertrauen mehr |
| 6 | Profil-Switcher absichern: Name sichtbar/Toast beim Wechsel, stärkerer Aktiv-Zustand | F18 | 1–2 h | Falsch-Profil-Unfälle (WAF-Klassiker) verhindert |
| 7 | Sheet-Dirty-Check: „Eingaben verwerfen?" vor Schließen mit Inhalt | F19 | 2 h | Keine still verlorenen Eingaben |
| 8 | Offline-Kaltstart-Fallback: gebrandete lokale Fehlerseite mit Retry statt weißer Wand | F2 | 2–4 h | Würdevolles Scheitern ohne Netz |
| 9 | Skeletons + 150-ms-Crossfade bei Seiten-/Profilwechsel; Reload-aus-Hintergrund nur bei neuer Version | F13 F14 F25 | ½ Tag | Gefühlte Native-Performance trotz Remote-Modus |
| 10 | Polish-Paket: 7 SVG-Icons fürs Mehr-Menü, Haptics an Kernaktionen, 44-pt-Targets, A11y (Focus-Trap, aria-live), Foto-Vorschau vor KI-Buchung | F8 F10 F15 F22 F23 | 1 Tag | Von „gut" zu „fühlt sich teuer an" |
6. Roadmap / Plan
Phase 0 — „Jaqueline-Gate" (vor Rollout auf ihr iPhone; ~1–1,5 Arbeitstage)
Kriterium: Kein Befund mehr, der beim normalen Tagesgebrauch die Illusion bricht oder Daten kostet.
- IPA-Paket (einmalig, alles zusammen): Icon + Splash (F1), Portrait-Lock (F3), Swipe-Back (F7), dabei
@capacitor/haptics+@capacitor/status-bargleich mit installieren (Nutzung kann später kommen — Plugins nachrüsten = neuer IPA-Build, also jetzt einpacken). - Externe Links → In-App-Safari (Top-2).
- Touch-CSS: user-select/touch-callout/hover (Top-3).
- Einstellungen app-bewusst (Top-4).
- Profil-Switcher-Feedback (Top-6) + Sheet-Dirty-Check (Top-7).
- Plan-Löschen-Undo + Listen-Rollback (aus Top-5, der schnelle Teil).
- Geräte-Test auf Dennis' iPhone: Long-Press, Garmin-Link, Funkloch-Simulation (Flugmodus mitten im Abhaken), Swipe-Back mit offenem Sheet.
Danach erst SideStore + Pocket-ID-Erstlogin auf Jaquelines iPhone — zusammen mit dem ohnehin geplanten 14.06.-SideStore-Refresh-Check.
Phase 1 — Native-Feinschliff (Woche danach; ~1–2 Tage verteilt)
- Netz-Robustheit komplett: GET-Retry, Fehlertexte, einheitliches Optimistic-Pattern (Rest von Top-5).
- Offline-Kaltstart-Fallback (Top-8) — nach Klärung von Offener Frage 3.
- Skeletons + Crossfade + versionsbewusster Hintergrund-Reload (Top-9).
- Haptics an ~6 Kernaktionen, StatusBar-/Splash-Feintuning (F4/F8).
- Statistik-Charts auf dem Gerät verifizieren (F26), ggf. fixen.
Phase 2 — Delight & Hygiene (laufend, kein Blocker)
- SVG-Icon-Satz fürs Mehr-Menü + Desktop-Sidebar (F22).
- A11y-Runde: Focus-Trap/-Return, aria-live, Labels (F23).
- Foto-Logging mit Vorschau-Bestätigung (F15).
- Onboarding-Touches: Makro-Ziel-Hinweis, Tagebuch-Empty-State, Ersteinrichtungs-Checkliste (F20).
- Token-Hygiene: Sonar/
.row-Dedupe, Hardcodes auf Tokens, Inline-Margins (F24). - Kür: Pull-to-Refresh + Bounce-Entscheidung (F9), Page-Transitions, 44-pt-Pass.
Nicht tun (bewusste Anti-Empfehlungen): kein Landscape-Support nachbauen (sperren ist besser) · keinen Offline-Schreib-Sync-Layer erfinden, bevor Offene Frage 3 entschieden ist · kein Icon-Redesign der Inhalts-Emojis (Profile/Mahlzeiten/Metriken funktionieren) · keine SPA-Framework-Migration — Svelte 4 + Hash-Routing tragen das Zielbild problemlos.
7. Offene Fragen
- Benachrichtigungen in der App? Web-Push funktioniert in der WKWebView nicht, echtes APNs scheitert an der Gratis-Apple-ID (keine Push-Entitlements via SideStore). Optionen: (a) keine Pushes in der App, PWA bleibt stiller Push-Kanal, (b) lokale Notifications (Capacitor-Plugin, ohne Server-Push, z. B. Erinnerungen), (c) später bezahlter Dev-Account. Was ist der Anspruch?
- Bleibt die PWA auf den iPhones sichtbar? Zwei „Apps" mit demselben Inhalt auf Jaquelines Homescreen wären verwirrend — PWA als dokumentierter Notfall-Fallback (SideStore-Ablauf!) behalten, aber vom Homescreen nehmen?
- Offline-Anspruch: Reicht „würdevoll scheitern" (Fallback-Seite + ehrliche Toasts), oder soll Eintragen im Funkloch gepuffert werden (Queue mit Nach-Sync)? Letzteres ist deutlich mehr Arbeit (Konflikte, Wir-Modus!) — Empfehlung: erst Phase 1 erleben, dann entscheiden.
- Scroll-Gefühl: Bounce + Pull-to-Refresh aktivieren (nativer, aber Verhaltensänderung) oder bewusst beim ruhigen, festen Scrollen bleiben?
- iPad:
Info.plistdeklariert iPad-Orientierungen, die UI ist aber rein Phone-first. iPad explizit ausschließen (Targeted Device Family) oder ist Jaquelines/Familien-iPad ein künftiger Use-Case? - Statistik-Charts (F26): Rendern sie auf echten Geräten — oder ist die Seite aktuell faktisch leer?
- Einkaufsliste im „Mehr"-Menü: Wenn Jaqueline primär einkauft und isst, gehört die Liste dann nicht in die Haupt-Tab-Leiste (z. B. statt „Gerichte" oder als 5. Tab statt „Mehr"-Slot)? Nutzungsdaten nach 2–3 Wochen entscheiden lassen.
- SideStore-Betrieb für Jaqueline (F21): Wer refresht ihr Gerät, was ist die Routine, was sagt man ihr, wenn die App nicht startet? (Direkt mit dem 14.06.-Check beantworten.)
Audit erstellt am 07.06.2026 · Screenshots: Chromium-Sweep 390×844@2x gegen den Live-Stand (127.0.0.1:9300, interner Token) · Code-Stand: main 9c306d0.