Files
oikos/docs/superpowers/specs/2026-03-30-ux-polish-design.md
T
Ulas fd5ffe3380 docs: add UX polish design spec (4-layer approach)
Defines the UX improvement strategy for Oikos post-v0.1.0:
Layer 1 — Design language & consistency (tokens, typography, module accents)
Layer 2 — Animations & transitions (page transitions, staggering, micro-interactions)
Layer 3 — Mobile PWA & native feel (install prompt, scroll, vibration, keyboard)
Layer 4 — Forms & modals (auto-focus, inline validation, bottom sheets, submit feedback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 16:23:28 +02:00

225 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UX Polish — Design-Spezifikation
**Datum:** 2026-03-30
**Status:** Genehmigt
**Scope:** UX-Verbesserungen (Phase 1 vor Featureerweiterungen)
---
## Ausgangslage
Oikos v0.1.0 ist funktional vollständig und alle 146 Tests sind grün. Das UI wirkt jedoch steril und beliebig — es fehlt eine eigene Persönlichkeit. Zusätzlich gibt es Konsistenzlücken zwischen Modulen, abrupte Übergänge, ein suboptimales mobiles Erlebnis und verbesserungswürdige Formular-UX.
Die Verbesserungen erfolgen in vier aufeinander aufbauenden Schichten (Layer for Layer), sodass jede Schicht das Fundament der nächsten bildet.
---
## Schicht 1 — Design-Sprache & Konsistenz
### Ziel
Der App eine eigene, wiedererkennbare Persönlichkeit geben — klar und präzise als Hauptrichtung, mit einem Hauch Wärme und Familiarität.
### Typografie-Skala
Vier klar unterscheidbare Stufen ersetzen die aktuelle flache Hierarchie:
| Stufe | Größe | Gewicht | Einsatz |
|-------|-------|---------|---------|
| Display | 24px | 700 | Seitentitel, Modal-Titel |
| Title | 18px | 600 | Widget-Überschriften, Gruppen-Header |
| Body | 15px | 400 | Fließtext, Listeneinträge |
| Caption | 13px | 400 | Metadaten, Zeitstempel, Labels |
Überschriften (Display, Title) erhalten `letter-spacing: -0.3px` für einen modernen, knappen Look.
### Farb-Tokens
Die Grautöne erhalten einen minimalen Warmton-Shift, um den Charakter von "Tech-App" zu "Familien-App" zu verschieben:
```css
/* Vorher → Nachher */
--color-bg: #F5F5F7 #F6F5F3 /* ganz leicht warmer Tint */
--color-surface: #FFFFFF #FFFFFF /* bleibt rein */
--color-text-primary:#1C1C1E #1A1A1F /* minimal wärmer */
```
Der Accent-Blau (`#007AFF`) bleibt unverändert. Dunkel-Modus erhält analoge Anpassungen.
### Komponenten-Konsistenz
Alle Module erhalten identische Card-Tokens:
- Padding: `16px` überall (aktuell variiert zwischen 12px20px je Modul)
- Schatten: `shadow-sm` im Ruhezustand, `shadow-md` bei Hover
- Buttons: `:hover` = leichter Helligkeitsshift, `:active` = `scale(0.97)` (haptisches Feedback-Gefühl)
### Modul-Akzentfarben
Jedes Modul erhält eine dezente, eigene Akzentfarbe für Page-Header und FAB. Die Farben sind bereits in der Architektur vorgesehen (theme-color), werden aber vervollständigt und konsequent eingesetzt:
| Modul | Akzent |
|-------|--------|
| Dashboard | `#007AFF` (Standard-Blau) |
| Aufgaben | `#FF9500` (Orange) |
| Einkauf | `#34C759` (Grün) |
| Essensplan | `#FF6B35` (Warm-Orange) |
| Kalender | `#5AC8FA` (Hellblau) |
| Notizen | `#FFCC00` (Gelb) |
| Kontakte | `#AF52DE` (Violett) |
| Budget | `#30B0C7` (Teal) |
---
## Schicht 2 — Animationen & Übergänge
### Ziel
Die App fühlt sich lebendig an. Alle Animationen respektieren `prefers-reduced-motion: reduce`.
### Seitenübergänge
Neue Seite fährt von rechts ein, alte geht nach links raus:
- Transform: `translateX(24px) → translateX(0)`
- Opacity: `0 → 1`
- Dauer: `200ms`, Easing: `ease-out`
- Zurück-Navigation: gespiegelte Richtung
Implementierung im zentralen `router.js` — kein Modul-Code nötig.
### Gestaffelte Listen-Einblendung (Staggered Fade-In)
Beim initialen Laden einer Seite erscheinen Listenelemente und Cards nacheinander:
- Jedes Item: `opacity 0 → 1` + `translateY(8px) → 0`
- Verzögerung: 30ms pro Item, maximal 5 Items gestaffelt (danach sofort)
- Dauer pro Item: `180ms`
### Micro-Interactions
**Checkbox (Aufgaben erledigt):**
Das SVG-Häkchen zeichnet sich per `stroke-dashoffset`-Animation ein (60ms). Die Karten-Zeile bekommt einen `text-decoration: line-through`-Transition (100ms).
**FAB:**
`scale(0.92)` beim `:active` + Ripple-Effekt (radial expandierender Kreis, 300ms, opacity 0→1→0).
**Swipe-Reveal (Aufgaben):**
Aktuell erscheint der farbige Hintergrund abrupt. Neu: Hintergrundfarbe und Icon blenden proportional zur Swipe-Distanz ein (`opacity: swipeDistance / SWIPE_THRESHOLD`).
### Skeleton-Loading
Dashboard-Widgets zeigen beim Laden animierte Skeleton-Platzhalter:
- Shimmer-Animation via `@keyframes` (linearer Gradient läuft durch, 1.4s, unendlich)
- Schematische Rechtecke in Card-Form, passend zur jeweiligen Widget-Größe
- Ersetzt leere Flächen während des API-Calls
### Empty States
Jede leere Liste erhält einen Inline-SVG-Platzhalter und einen kontextuellen CTA:
| Modul | Text | CTA |
|-------|------|-----|
| Aufgaben | "Keine Aufgaben — alles erledigt?" | "+ Aufgabe erstellen" |
| Einkauf | "Die Liste ist leer" | "+ Artikel hinzufügen" |
| Essensplan | "Kein Essen geplant" | "Mahlzeit eintragen" |
| Notizen | "Noch keine Notizen" | "+ Notiz erstellen" |
| Kontakte | "Noch keine Kontakte" | "+ Kontakt hinzufügen" |
| Budget | "Keine Buchungen diesen Monat" | "+ Buchung eintragen" |
SVGs sind kleine, themenbezogene Illustrationen (Linien-Icons, kein Clipart), inline im HTML, kein externer Fetch.
---
## Schicht 3 — Mobile PWA & Natives Gefühl
### Ziel
Die installierte App fühlt sich auf dem Handy nativ an.
### PWA-Install-Prompt
**Timing:** Prompt erscheint erst nach 23 Benutzerinteraktionen (z.B. nach dem ersten erfolgreich erstellten Eintrag), nicht sofort beim ersten Seitenaufruf.
**Darstellung:** Bottom Sheet von unten einfahrend (nicht abruptes Banner). Enthält App-Icon, Name "Oikos", kurzen Nutzentext.
**Wiederholung:** Einmal abgelehnt → 7 Tage nicht erneut zeigen (via `localStorage` mit Timestamp).
**Plattformspezifisch:**
- Android: natives `beforeinstallprompt`-Event
- iOS: eigene Anleitung ("Teilen → Zum Home-Bildschirm") da kein natives Event
### Scroll & Overscroll
Auf allen scrollbaren Containern:
- `overscroll-behavior: contain` — verhindert Browser-Pull-to-Refresh innerhalb der App
- `-webkit-overflow-scrolling: touch` — Momentum-Scrolling auf iOS
- Bottom Nav und Header: `position: sticky` mit `env(safe-area-inset-bottom)` — kein Layout-Shift durch dynamische Viewport-Höhe (iOS Safari)
### Vibrations-Feedback
`navigator.vibrate()` bei bedeutsamen Aktionen, nur wenn API verfügbar und `prefers-reduced-motion` nicht gesetzt:
| Aktion | Muster |
|--------|--------|
| Aufgabe erledigt | `10ms` |
| Swipe-Aktion ausgelöst | `15ms` |
| Eintrag gelöscht | `[30, 50, 30]ms` |
| Fehlermeldung | `[20, 40, 20]ms` |
### Keyboard-Verhalten (Virtuelles Keyboard)
Beim Tippen in ein Eingabefeld springt dieses automatisch in den sichtbaren Bereich:
```js
input.addEventListener('focus', () => {
setTimeout(() => input.scrollIntoView({ behavior: 'smooth', block: 'center' }), 300);
});
```
300ms Verzögerung gibt dem Keyboard Zeit, sich zu öffnen.
### Theme-Color
Dynamische `theme-color` Meta-Tag-Aktualisierung beim Modulwechsel wird vervollständigt — jedes Modul übergibt beim Rendern seine Akzentfarbe, die Browser-Chrome-Farbe wechselt entsprechend.
---
## Schicht 4 — Formulare & Modals
### Ziel
Eingaben sind schnell, klar und fehlertolerant — besonders auf Mobil.
### Auto-Fokus & Tastaturnavigation
- Beim Öffnen eines Modals: Fokus springt automatisch auf erstes Eingabefeld (`setTimeout(0)` nach Modal-Render)
- `Tab`: logische Feldreihenfolge (entspricht DOM-Reihenfolge)
- `Enter` in einzeiligen Inputs: springt zum nächsten Feld
- `Enter` im letzten Feld (oder Textarea + `Ctrl+Enter`): löst Submit aus
- `Escape`: schließt Modal
### Inline-Validierung
- Trigger: `blur`-Event auf jedem Feld (nicht erst bei Submit)
- Fehlermeldung: direkt unter dem Feld, `color: var(--color-danger)`, mit Warn-Icon
- Erfolgreiche Pflichtfelder: dezenter grüner Rand (`border-color: var(--color-success)`)
- Submit-Button: deaktiviert solange Pflichtfelder leer, aktiv sobald Minimalanforderungen erfüllt
### Modal-UX auf Mobil
Auf Screens < 768px werden Modals als **Bottom Sheet** dargestellt:
- Einfähranimation: `translateY(100%) → translateY(0)`, 250ms, `ease-out`
- Maximalhöhe: `90dvh`, intern scrollbar
- Swipe-to-Close: Swipe nach unten > 80px schließt Modal; zwischen 080px gibt es gummibandartigen Widerstand (`transform: translateY(distance * 0.4)`)
- Backdrop-Klick: schließt Modal
- Schließanimation: `translateY(0) → translateY(100%)`, 200ms
Auf Desktop (≥ 768px): zentriertes Modal bleibt unverändert (Backdrop-Klick + Escape schließen).
### Submit-Feedback
**Erfolg:**
1. Submit-Button: Label wird durch Checkmark-Icon ersetzt (600ms)
2. Modal schließt sich mit Slide-Down-Animation
3. Liste aktualisiert sich (optimistisch oder via Re-Fetch)
**Fehler:**
1. Submit-Button: `shake`-Animation (300ms, ±4px horizontal)
2. Fehlermeldung erscheint unter dem betreffenden Feld oder als Banner oben im Modal
3. Kein Datenverlust — alle eingegebenen Werte bleiben erhalten
---
## Nicht in Scope
- Neue Features (Meal Drag&Drop, Budget-Recurrence, Kalender-Auto-Sync) — diese kommen erst nach UX + Code-Qualität
- Backend-Änderungen — alle vier Schichten sind rein frontend-seitig
- Push-Benachrichtigungen — explizit v1.1 (BACKLOG)
- Grundlegende Architekturänderungen am Router oder API-Layer
---
## Reihenfolge der Implementierung
1. Schicht 1: `tokens.css`, `reset.css`, `layout.css`, alle Modul-CSS-Dateien
2. Schicht 2: `router.js` (Seitenübergänge), alle Page-Module (Staggering, Micro-Interactions), `dashboard.js` (Skeleton)
3. Schicht 3: `oikos-install-prompt.js`, `sw.js`, alle Page-Module (Scroll, Keyboard, Vibration)
4. Schicht 4: `components/modal.js`, alle Page-Module (Formulare, Validierung)
Jede Schicht ist ein eigener Commit-Block und kann unabhängig reviewt werden.