Files
mhoennigandClaude Fable 5.1 6471608aa4 Start UI refresh: shared Werkator tokens and selector inventory (0.1.100)
Mark #ui-refresh as in progress. Rewrite styles.css around one token set
(light-dark, Lumo derived from it) including the classes for the target
structure of docs/plans/ui-refresh.md; move login.css and offline.html to
the same tokens. Add the test selector inventory (step 1) and a handover
section so a goal run can continue with steps 3 to 8. Views unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 10:53:05 +02:00

181 lines
24 KiB
Markdown
Raw Permalink 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.
# Spezifikation: UI-Refresh — Werkator-Optik und mobile Bedienbarkeit
Status: geplant, nicht begonnen. Werkbaum-Knoten `#ui-refresh` unter `#experience`.
Entwürfe: [mockups/ui-refresh.html](mockups/ui-refresh.html) (im Browser öffnen; Umschalter für Hell/Dunkel oben rechts).
Nachfolger: [auth-flow-refresh.md](auth-flow-refresh.md) baut auf diesem Stand auf.
## 0. Übergabestand für den Goal-Lauf (2026-09-12)
Vorbereitet und committet, hier weitermachen, nichts davon wiederholen:
- Branch `ui-refresh` existiert lokal (von `main` bei 7bfed1a), `#ui-refresh` steht im Werkbaum auf `[~]`. Der Branch ist noch nicht gepusht; `git push -u origin ui-refresh` beim ersten Push.
- Schritt 1 (Selektor-Inventar) liegt in [ui-refresh-selectors.md](ui-refresh-selectors.md). Schritt 2 (Tokens) ist umgesetzt: `styles.css` ist komplett neu geschrieben (ein Token-Set, Lumo-Ableitung, Klassen für die Zielstruktur aus Abschnitt 5 sind bereits enthalten, u. a. `wj-tiles`, `wj-tile`, `wj-stepper`, `journal-entry-row`, `wj-day-nav`, `wj-hours-pills`, `wj-sheet`, `wj-calendar-kpis`), `login.css` und `offline.html` nutzen dieselben Tokens. Die Kotlin-Views sind noch unverändert; die Schritte 3 bis 8 stehen aus. Wo die CSS-Klassen nicht zur gebauten Struktur passen, gilt die Spezifikation, und das CSS wird angepasst.
- Lokale Toolchain: `export JAVA_HOME=$HOME/opt/jdk25` (gepinntes Temurin 25 aus `packaging/jdk.properties`, geprüft installiert). Playwright-Browser liegen unter `~/.cache/ms-playwright`. Vollständiger Lauf: `./gradlew --no-daemon --console=plain test -Pproduction=true`, danach `./tools/test-frontend` und `./gradlew --no-daemon firefoxAcceptance productionAcceptance -Pproduction=true -x test`.
- CI (Werkator) baut und deployt nur `main`: https://werkator.javagil.de/repos/werkjournal/latest (ein Lauf dauert etwa zehn Minuten). Feature-Branches laufen nicht in CI; deshalb vor dem Merge lokal die vollständige Pipeline grün haben.
- Abschluss dieses Pakets: `git checkout main && git merge --no-ff ui-refresh`, Micro-Version erhöhen, pushen, dann den CI-Lauf auf der Werkator-Seite bis zum Ende beobachten (Status success und Deployment). Bei Fehlschlag auf `main` beheben und erneut pushen; kein Force-Push. Danach mit [auth-flow-refresh.md](auth-flow-refresh.md) fortfahren (eigener Goal-Lauf).
- `.claude/launch.json` ist lokal und nicht Teil des Repos (dient nur der Mockup-Vorschau).
## 1. Ziel
Die PWA soll auf dem Smartphone leicht, schnell und „schick“ wirken und auf dem Desktop ruhig und aufgeräumt. Farbwelt und Tonalität orientieren sich an https://werkator.javagil.de (Petrol-Akzent auf neutralem, leicht grünstichigem Grau, Clay und Amber als Nebenfarben, hell und dunkel). Home (Today), Arbeitszeit-Erfassung und Habits sind die Kernscreens und bekommen den größten Anteil der Arbeit. Verhalten, Datenmodell und Anmeldung bleiben unverändert; das ist eine Gestaltungs- und Bedienbarkeitsarbeit.
## 2. Ausgangslage
- Vaadin Flow mit Lumo. Gestaltung liegt in `src/main/resources/META-INF/resources/styles.css` (zwei historische Schichten mit unterschiedlichen Petrol-Tönen `#086b78` und `#0c4654`) und in `src/main/resources/static/login.css` (eigener Ton `#17677b`, eigene Variablen). Es gibt keine gemeinsamen Design-Tokens.
- Shell: `ui/MainLayout.kt` mit Navbar (Logo, Titel, Quick-Nav, Drawer-Toggle) und mobiler Bottom-Nav mit drei Icons ohne Beschriftung plus Drawer-Toggle.
- Today: `ui/TodayView.kt`, dunkle Arbeitskarte mit 52–70 px Stundenzahl, Wochen-/Monatssumme, Details-Element mit Einträgen, Habit-Karten mit Zähler und Details.
- Arbeit: `work/WorkView.kt`, Datumsauswahl, Summenkarte, „Freier Tag“-Karte, Einträge als Karten mit vier Aktionsbuttons in einer Reihe, Formular in einem 600 px-Dialog.
- Habits: `habits/HabitView.kt` (Auswahlbuttons, Monatskalender, Konfigurationsdialog) und `habits/HabitCounter` (Karte mit Zähler ±, Grenzen als Textzeilen).
- Weitere Seiten: `work/ReportsView.kt`, `catalog/CatalogView.kt`, `ui/SettingsView.kt`, `identity/AdminView.kt`, `offline.html`, Wartungsseite.
- Wahrgenommene Probleme: große Paddings und Radien wirken klobig; Stundenzahl dominiert; Aktionsreihen mit gleichwertigen Icon-Buttons; Bottom-Nav ohne Labels; Details-Elemente verstecken Inhalt; Dialoge statt Bottom-Sheets; Habit-Grenzen als Fließtext statt Anzeige; Kalenderzellen ohne Zielbezug.
## 3. Nicht-Ziele
- Keine Änderung an Anmeldung, Einladungen, Sessions oder E-Mails (siehe Nachfolgespezifikation). Die Login-Seite wird nur farblich und typografisch nachgezogen, ihre Struktur bleibt.
- Keine Schema-Änderung, keine Migration, keine neuen Fachfunktionen.
- Keine Offline-Erfassung, keine Timer, keine Swipe-Gesten mit versteckten Aktionen.
- Kein Wechsel weg von Vaadin Flow, keine kostenpflichtigen Komponenten, keine neuen npm-Abhängigkeiten ohne begründete Notwendigkeit.
## 4. Verbindliche Vorgaben
### 4.1 Arbeitsweise
- Branch `ui-refresh` von `main`. Vor Beginn `#ui-refresh` im Werkbaum auf `[~]` setzen, committen, pushen. Vor jedem Push die Micro-Version in `build.gradle.kts` erhöhen.
- Kleine, thematisch geschlossene Commits in der Reihenfolge aus Abschnitt 7. Jeder Commit lässt `./gradlew test -Pproduction=true` grün.
- Ergebnisdokument `docs/implementation/ui-refresh.md` im Stil der vorhandenen Implementierungsdokumente (Was wurde gebaut, welche Entscheidungen, Abschnitt „Verification on <Datum>“ mit tatsächlichen Testläufen und geprüften Screenshots). Kein Erfolg behaupten, der nicht geprüft wurde.
- Nach Abschluss `#ui-refresh` auf `[x]`, Werkbaum-Notiz mit Datum, README-Absatz zur Gestaltung aktualisieren.
- Bei Rückfragen, die die Gestaltung wesentlich ändern würden, anhalten. Kleine Entscheidungen selbst treffen und im Ergebnisdokument festhalten.
### 4.2 Stabilitätsregeln
- Alle `data-*`-Attribute, `role`-Attribute, `aria-label`-Texte und Klassennamen, die Tests verwenden, bleiben bestehen (`data-work-entry`, `data-work-field`, `data-work-submit`, `data-work-discard`, `data-work-recovery`, `aria-pressed`, `role=alert`, `role=status`, `.journal-entry`, `.summary-card`, `.habit-indicator`, `.habit-warning`, `.calendar-day`, `.calendar-today`, `.work-entry-selected` u. a.). Vor dem Umbau die Browser-Schritte unter `src/test/kotlin/.../acceptance` nach verwendeten Selektoren durchsuchen und diese Liste im Ergebnisdokument aufführen.
- i18n-Schlüssel dürfen ergänzt werden; bestehende Schlüssel bleiben. Neue Texte immer in `messages_de.properties` und `messages_en.properties`.
- Alle Chromium- und Firefox-Szenarien laufen weiterhin durch. Wo ein Szenario nur wegen eines geänderten Layouts scheitert, wird der Test-Schritt angepasst, nicht das Szenario gestrichen.
- Draft-Handling (`work-drafts.js`), Push-Einstellungen, Zeitzonen-Erkennung und Theme-Wahl (`device-settings.js`) bleiben funktional unverändert.
### 4.3 Design-Tokens (Werkator)
Ein gemeinsames Token-Set wird in `styles.css` (auf `html`) und `login.css` (auf `:root`) identisch definiert. Lumo-Variablen werden daraus abgeleitet, nicht doppelt gepflegt. Die dunkle Variante folgt der bestehenden Theme-Wahl der App (Attribut/Klasse, die `device-settings.js` setzt) und `prefers-color-scheme`; `light-dark()` bleibt zulässig, solange die Theme-Wahl der Einstellungen weiter greift.
| Token | Hell | Dunkel | Verwendung |
|---|---|---|---|
| `--wj-bg` | `#ffffff` | `#242928` | Seitengrund |
| `--wj-panel` | `#fafafa` | `#2a302f` | Karten, Navbar, Bottom-Nav |
| `--wj-panel-2` | `#f4f5f5` | `#313837` | Eingebettete Flächen, Chips, Stepper |
| `--wj-line` | `#e4e7e7` | `#3e4645` | Trennlinien, Kartenrahmen |
| `--wj-line-2` | `#d1d6d5` | `#545f5d` | Betonte Rahmen, Fokus-Hintergrund |
| `--wj-text` | `#141616` | `#f4f5f5` | Haupttext |
| `--wj-text-2` | `#3e4645` | `#d1d6d5` | Sekundärtext |
| `--wj-muted` | `#6e7c7a` | `#9ea9a7` | Labels, Eyebrows, Hinweise |
| `--wj-ghost` | `#9ea9a7` | `#6e7c7a` | Deaktiviertes, Platzhalter |
| `--wj-accent` | `#0e6b7d` | `#63bccb` | Primäraktion, aktive Navigation, Zahlen |
| `--wj-accent-2` | `#0a5262` | `#8ed0db` | Hover/Pressed der Primäraktion |
| `--wj-accent-soft` | `#e3f0f3` | `#1f3b41` | Aktiv-Hintergrund, Fortschrittsspur, Chips aktiv |
| `--wj-accent-soft-2` | `#c6e0e6` | `#27505a` | Stärkere Akzentfläche |
| `--wj-clay` | `#b0563b` | `#e09070` | Warnhinweise (Grenze verfehlt), Löschen |
| `--wj-clay-soft` | `#f6e9e4` | `#3e2a22` | Hintergrund von Warnhinweisen |
| `--wj-amber` | `#b8860b` | `#e3b341` | Aufmerksamkeit (Entwurf vorhanden, Konflikt) |
| `--wj-amber-soft` | `#f9f0d2` | `#3d3418` | Hintergrund dazu |
| `--wj-crit` | `#b3261e` | `#ef6f66` | Fehler |
| `--wj-crit-soft` | `#f8e2df` | `#45201d` | Hintergrund von Fehlern |
| `--wj-shadow` | `0 1px 2px rgb(20 22 22 / .06), 0 4px 16px rgb(20 22 22 / .06)` | `0 2px 6px rgb(0 0 0 / .25)` | Schwebende Flächen (Sheet, Dialog) |
Lumo-Ableitung mindestens: `--lumo-primary-color: var(--wj-accent)`, `--lumo-primary-text-color: var(--wj-accent)`, `--lumo-primary-color-10pct: var(--wj-accent-soft)`, `--lumo-base-color: var(--wj-panel)`, `--lumo-body-text-color: var(--wj-text)`, `--lumo-secondary-text-color: var(--wj-muted)`, `--lumo-tertiary-text-color: var(--wj-ghost)`, `--lumo-contrast-10pct` und `-20pct` aus `--wj-line`/`--wj-line-2`, `--lumo-error-color: var(--wj-crit)`, `--lumo-error-text-color: var(--wj-crit)`, `--lumo-error-color-10pct: var(--wj-crit-soft)`, `--lumo-success-color: var(--wj-accent)`. Die alten Journal-Variablen (`--journal-*`) werden auf die neuen Tokens umgestellt oder entfernt; am Ende gibt es nur ein Token-Set.
### 4.4 Typografie, Rhythmus, Formen
- Schrift: `system-ui, -apple-system, "Segoe UI", Roboto, sans-serif`, Grundgröße 16 px, Zeilenhöhe 1.45. Tabellarische Ziffern (`font-variant-numeric: tabular-nums`) für alle Zahlen.
- Skala (Mobil / Desktop): Eyebrow 12/12 px Versalien, Buchstabenabstand 0.06em; Seitentitel 24/32 px, Gewicht 600, Buchstabenabstand −0.02em; Kartentitel 15/16 px, Gewicht 600; Hero-Zahl 40/56 px, Gewicht 600; Sekundärzahl 20/24 px; Fließtext 15/16 px; Hinweise 13 px.
- Radien: Karten 14 px, Buttons und Felder 10 px, Chips und Pills 999 px, Bottom-Sheet oben 20 px. Keine 20–28 px-Radien mehr.
- Abstände: 8-px-Raster. Seitenrand 16 px mobil, 32 px Desktop. Kartenpadding 16 px mobil, 20 px Desktop. Abstand zwischen Karten 12 px mobil, 16 px Desktop. Maximale Inhaltsbreite 1100 px, Zwei-Spalten-Layout ab 800 px (bestehendes Verhältnis 1.15 : 1).
- Flächen: Karten mit 1 px `--wj-line` auf `--wj-panel`, kein Schatten. Nur Bottom-Sheet und Dialoge haben `--wj-shadow`. Die dunkle Petrol-Arbeitskarte entfällt; die Hero-Karte auf Today ist eine helle Panel-Karte mit Akzentzahl (siehe Entwurf).
- Bewegung: Übergänge 150 ms `ease-out` für Hover/Fokus, 220 ms für Sheet-Öffnen. Bei `prefers-reduced-motion: reduce` keine Bewegung.
### 4.5 Interaktion und Touch
- Mindesttrefferfläche 44 × 44 px für alle Aktionen; Stepper-Buttons 48 × 48 px.
- Genau eine Primäraktion pro Screen als gefüllter Akzent-Button; alle übrigen Aktionen als leise Buttons (Text in `--wj-text-2`, transparent, Hover `--wj-panel-2`) oder als Icon-Buttons mit Tooltip und `aria-label`.
- Auf Bildschirmen ≤ 600 px öffnen Formulare als Bottom-Sheet (Vaadin `Dialog` mit Thema/Klasse `wj-sheet`: unten angedockt, volle Breite, Radius oben 20 px, Griffleiste, Inhalt scrollt im Sheet, Aktionen fest am unteren Rand mit Safe-Area). Ab 601 px als zentrierter Dialog mit 560 px Breite.
- Leseabstand zur Bottom-Nav: Seiteninhalt endet mindestens 96 px plus `env(safe-area-inset-bottom)` über dem unteren Rand.
- Fokus: 2 px Umriss in `--wj-accent` mit 2 px Versatz, sichtbar auf allen Flächen.
### 4.6 Barrierefreiheit
- Kontrast mindestens WCAG AA (4.5 : 1 Text, 3 : 1 große Zahlen und Icons) in beiden Themes. Kombinationen im Ergebnisdokument mit gemessenen Werten belegen (z. B. `--wj-accent` auf `--wj-panel`, `--wj-muted` auf `--wj-panel`, `--wj-clay` auf `--wj-clay-soft`).
- Alle Icon-Buttons behalten sichtbaren Tooltip und `aria-label`. Bottom-Nav-Links bekommen sichtbare Labels.
- Zustände (aktiv, Warnung, Fehler) nie nur über Farbe: zusätzlich Text, Icon oder Form.
## 5. Screens
Die Entwürfe in `mockups/ui-refresh.html` sind maßgeblich für Anordnung und Gewichtung; Pixelgleichheit ist nicht gefordert. Der Abschnitt „Voller Zustand, kompakt“ ist die Zielvorgabe; die „erste Fassung“ darunter zeigt nur die Herleitung und gilt dort nicht, wo sie abweicht.
### 5.1 Shell
- Navbar (alle Breiten): Logo 28 px, Wortmarke „werkjournal“ 18 px Gewicht 600, rechts Quick-Nav (Desktop) und Menü-Button. Höhe 56 px, Hintergrund `--wj-panel`, 1 px Linie unten. Gast-Badge als Pill in `--wj-accent-soft` bleibt (Desktop) und wandert mobil in den Drawer-Kopf.
- Bottom-Nav (≤ 600 px): vier Ziele „Heute“, „Arbeit“, „Habits“, „Mehr“ (Drawer) mit Icon 22 px über Label 11 px. Aktives Ziel: Icon und Label in `--wj-accent`, Pill-Hintergrund `--wj-accent-soft` hinter dem Icon. Höhe 64 px plus Safe-Area. `aria-current="page"` auf dem aktiven Link.
- Drawer: gleiche Einträge wie heute (Heute, Arbeit, Berichte, Habits, Kunden & Projekte, Einstellungen, Admin falls berechtigt) als Liste mit Icon, 48 px Zeilen, aktiver Eintrag mit `--wj-accent-soft`. Footer mit Version bleibt.
- Seitenkopf: Eyebrow (Datum oder Bereich) über Titel, darunter optional eine Zeile Sekundärtext. Kein 38 px-Titel mehr.
### 5.2 Today (Home)
Maßgeblich ist der Entwurf „Heute · voll, Kacheln“ mit vier Einträgen über drei Kunden und drei Habits; die Seite muss in diesem Zustand bei 390 × 812 ohne Scrollen vollständig sichtbar sein. Reihenfolge mobil von oben: Seitenkopf mit vollem Datum als Eyebrow und Titel „Heute“. Dann:
1. **Arbeitskarte (kompakt)**: links Kartentitel „Arbeit heute“ und direkt darunter die Tageszahl 34 px in `--wj-accent` mit Einheit in `--wj-muted`; rechts vertikal zentriert der Primär-Button „Erfassen“ (Plus-Icon, Text ab 360 px sichtbar), 44 px hoch, Pill-Form. Darunter eine Chip-Zeile mit den Tagessummen je Kunde („Hostsharing 4 h“), Chips in `--wj-panel-2`, Zahl in `--wj-accent`, bei mehr als vier Kunden horizontal scrollbar; bei nur einem Kunden entfällt die Zeile. Darunter zwei Kennzahlen „Diese Woche 23 h“ und „September 71 h“ (Label 12 px, Wert 16 px) mit je einer 4-px-Fortschrittsspur relativ zum Maximum der letzten zwölf Wochen bzw. Monate (Abfrage im `WorkService`, ohne Schemaänderung; ohne Vergleichswert bleibt die Spur leer). Freier Tag: statt der Spuren eine Pill „Freier Tag“ in `--wj-amber-soft`. Kartenpadding 14 × 16 px.
2. **Heutige Einträge**: keine Details-Klappe, keine Karten. Liste direkt unter der Arbeitskarte, jede Zeile 52 px mit 1 px Trennlinie: links „Kunde · Projekt“ (Kunde 600, Projekt in `--wj-muted`) und darunter Beschreibung 12 px in `--wj-muted`, einzeilig mit Ellipsis; rechts Inline-Stepper „− 3 h +“ (Buttons 34 px rund, Zahl in `--wj-accent`) und ein Pfeil in `--wj-ghost`. Stepper ändert die Stunden direkt (bestehende Regeln: max. 12 h/Tag, min. 1 h, Konflikt als Notification); Tippen auf den Textbereich öffnet den Eintrag auf der Arbeit-Seite (bestehender Link mit `entry`-Parameter, Klasse `journal-entry` bleibt). Leerzustand: eine Zeile „Noch nichts erfasst“ mit sekundärem Link „Jetzt erfassen“.
3. **Habits als Kacheln**: Abschnittstitel „Habits“ mit leisem Link „Kalender“ rechts. Habits stehen nebeneinander in einem Raster mit drei Spalten (Gap 8 px), jede Kachel in sich vertikal: Name 13 px (600, einzeilig mit Ellipsis, voller Name als `title`), Zähler 28 px, Statuszeile 11 px mit Punkt in Statusfarbe (z. B. „Ziel · Wo 4/5“ in `--wj-accent` mit Häkchen; „noch 1 · Wo 3/5“ in `--wj-clay`; „max 3 · noch 1“ in `--wj-muted`), darunter das große Plus (44 px rund, gefüllt Akzent) und darunter das kleine Minus (34 × 30 px Pill, leise). Erreichtes Tagesziel färbt den Zähler in `--wj-accent`. Mehr als drei Habits brechen in weitere Dreierzeilen um; bei zwei Habits bleiben zwei Spalten mit gleicher Kachelbreite, bei einem Habit eine Zeilenkarte wie in der Alternative. Tippen auf Name oder Zähler öffnet mobil ein Bottom-Sheet mit dem vollständigen `HabitCounter` (Grenzen, Wochenwert, Neu laden), Desktop ein Popover. Der Zähler bleibt in `HabitCounter`; `aria-label`, `role=status`/`alert`, `.habit-indicator` und `.habit-warning` bleiben erhalten.
Alternative (im Entwurf „Zeilen“ dokumentiert, nicht Standard): verdichtete Zeilenkarten 56 px mit Stepper rechts. Sie wird nur verwendet, wenn genau ein Habit aktiv ist.
4. Desktop (≥ 800 px): links Arbeitskarte und Eintragszeilen, rechts Habit-Kacheln, drei pro Zeile, Kachelpadding 14 px.
### 5.3 Arbeit (WorkView)
- Seitenkopf: Eyebrow „Arbeit“, Titel ist das gewählte Datum (z. B. „Freitag, 12. September“), darunter Tagesnavigation als drei Pills: „‹ Vortag“, „Heute“, „Folgetag ›“ (Folgetag deaktiviert, wenn Datum = heute) und ein Kalender-Icon-Button, der den bestehenden `DatePicker` öffnet. Der DatePicker bleibt als Komponente vorhanden (Tests), darf aber visuell hinter dem Icon liegen.
- Tageskarte (kompakt wie auf Today): Titel „Tag“, Tageszahl 34 px, rechts Primär-Button „Erfassen“ (deaktiviert für Zukunftstage, mit Tooltip-Begründung), Chip-Zeile mit Kundensummen, darunter in derselben Karte die Zeile „Freier Tag“ mit Schalter (Toggle mit Icon); Konfliktmeldung als `role=alert`-Zeile darunter. „Neu laden“ als leiser Icon-Button rechts im Kartentitel.
- Einträge: keine Karten mit Aktionsreihe mehr. Abschnittstitel „Einträge“ mit Hinweis „Tippen zum Bearbeiten“ in 12 px. Zeilen wie auf Today (52 px, Kunde · Projekt, Beschreibung einzeilig, Inline-Stepper, Pfeil); die ganze Zeile außer dem Stepper öffnet das Bearbeiten-Sheet. Attribut `data-work-entry` bleibt auf der Zeile; der per Link fokussierte Eintrag (`work-entry-selected`) bekommt Hintergrund `--wj-accent-soft` mit 10 px Radius statt eines Rahmens. Bestehende Aktivierungsregeln bleiben.
- Formular (Bottom-Sheet mobil, Dialog Desktop), kompakt: Kopf mit Titel „Erfassen“ / „Bearbeiten“; rechts im Kopf beim Bearbeiten ein Löschen-Icon (öffnet die bestehende Bestätigung) und der leise „Schließen (Entwurf behalten)“-Button. Felder: Kunde und Projekt nebeneinander in zwei Spalten (bestehender `CatalogPicker`, ab 360 px zweispaltig), Stunden als Pills 1–8 in einer Reihe plus „mehr“-Chip, der das `IntegerField` (1–12) einblendet (das Feld bleibt das Datenfeld mit `data-work-field="hours"`, die Pills setzen nur seinen Wert), Beschreibung als `TextArea` mit 56 px Mindesthöhe und Zeichenanzeige ab 1800/2000, zuletzt das Datum als Nur-Lesen-Zeile 40 px, die beim Tippen den `DatePicker` öffnet. Fußzeile fest am unteren Rand: Primär „Speichern“, leise „Verwerfen“. Entwurfs-Wiederherstellung (`data-work-recovery`) erscheint als Amber-Hinweiskarte oberhalb der Felder mit Buttons je Entwurf; Konfliktansicht als Amber-Karte mit „Aktueller Stand“ und Button „Meinen Entwurf übernehmen“.
### 5.4 Habits
- Seitenkopf: Eyebrow „Habits“, Titel „Kalender“.
- Habit-Auswahl: horizontal scrollbare Pill-Leiste (`aria-pressed` bleibt), aktive Pill gefüllt in `--wj-accent-soft` mit Akzenttext. Rechts davon (Desktop) bzw. darunter (mobil) drei leise Icon-Buttons: Anlegen, Bearbeiten, Löschen.
- Monatskopf: „‹“ Monatsname Jahr „›“ zentriert, Monatsname ist Button für die bestehende Monatswahl. Darunter Kennzahlen-Zeile: „Diesen Monat 21“, „Diese Woche 3 / 5“, „Serie 4 Tage“ (Serie = aufeinanderfolgende Tage mit Zähler ≥ Tagesminimum bzw. ≥ 1 ohne Minimum, endend heute oder gestern; im `HabitService` berechnen, keine Schemaänderung).
- Kalender: 7 Spalten, Zellen quadratisch (mobil ≈ 44 px, Desktop 56 px), Radius 10 px. Zelle zeigt Tagesnummer 12 px oben links und Zählwert 16 px zentriert; Hintergrund abgestuft: 0 → `--wj-panel-2`, unter Tagesminimum → `--wj-clay-soft`, im Ziel → `--wj-accent-soft`, über Tagesmaximum → `--wj-amber-soft`; ohne Grenzen: 0 → `--wj-panel-2`, > 0 → `--wj-accent-soft`. Heute mit 2 px Akzentrahmen (`calendar-today`), Nachbarmonat 45 % Deckkraft, Zukunft nicht klickbar. Wochenzeile rechts mit Wochensumme (bestehende Wochensummen-Anzeige bleibt, kompakter).
- Tages-Detail: Tippen auf eine Zelle öffnet mobil ein Bottom-Sheet mit dem `HabitCounter` für diesen Tag (Desktop: Popover-Dialog), inklusive Grenzen und Neu laden.
- Konfigurationsdialog (Name, Aktiv, vier Grenzen): als Sheet/Dialog mit Feldern in zwei Spalten (Tag min/max, Woche min/max) ab 400 px Breite, Hilfetext `habit.limitHelp` als kleiner Absatz.
### 5.5 Übrige Seiten
Berichte, Kunden & Projekte, Einstellungen und Admin übernehmen nur Tokens, Rhythmus, Kartenstil, Button-Hierarchie und Seitenkopf. Keine Neuanordnung. Balken in Berichten nutzen `--wj-accent` auf `--wj-accent-soft`. Die Admin-Seite wird in der Nachfolgespezifikation neu geschnitten.
### 5.6 Login-Seite, Offline- und Wartungsseite
`login.css`, `offline.html` und die Wartungsseite übernehmen dasselbe Token-Set, Schrift, Radien 14/10 px, Button-Hierarchie und Fokusstil. Die Karte der Login-Seite bekommt 24 px Padding mobil, Radius 14 px, keinen 80 px-Schatten. Struktur, Formulare, IDs und Texte bleiben unverändert; alle Workflow-Änderungen kommen mit `auth-flow-refresh.md`.
## 6. Technische Umsetzungshinweise
- CSS ausschließlich in `styles.css` und `login.css`; Kotlin-Views setzen Klassen und Struktur. Keine Inline-Styles außer für berechnete Fortschrittsbreiten (`style="--wj-progress: 62%"`).
- Bottom-Sheet: Vaadin `Dialog` mit `addClassName("wj-sheet")` und `::part(overlay)`-Styling (`position: fixed; inset: auto 0 0 0; border-radius: 20px 20px 0 0; max-height: 92dvh`), Klasse nur ≤ 600 px wirksam. Header und Footer des Dialogs verwenden.
- Stunden-Pills: `Div` mit Buttons `aria-pressed`, die den Wert des `IntegerField` setzen; Wert-Änderungen am Feld (auch durch Draft-Wiederherstellung) synchronisieren die Pills.
- Bottom-Nav-Labels: bestehende `RouterLink`s um `Span` mit Text erweitern; `aria-label` beibehalten.
- Mockups sind statische HTML-Dateien und werden nicht ausgeliefert; nichts aus `docs/plans/mockups` in `src` kopieren, nur die Werte übernehmen.
- Vaadin-Komponenten, bei denen Lumo-Styling nicht ausreicht, über `::part(...)` stylen; kein Shadow-DOM-Piercing über Skripte.
## 7. Umsetzungsreihenfolge
1. Selektor-Inventar der Browser-Tests erstellen und im Ergebnisdokument festhalten. Werkbaum `[~]`, Version, Push.
2. Tokens: gemeinsames Token-Set in `styles.css` und `login.css`, Lumo-Ableitung, alte Variablen umstellen. Screenshots hell/dunkel als Referenz.
3. Shell: Navbar, Bottom-Nav mit Labels, Drawer, Seitenkopf, Sheet-Basisstil.
4. Today: Hero-Karte mit Kennzahlen und Spuren (Service-Abfrage), Eintragsliste, kompakte Habit-Karten.
5. Arbeit: Tagesnavigation, Tageskarte mit Freier-Tag-Toggle, Eintragskarten, Formular als Sheet mit Stunden-Pills, Entwurfs-/Konfliktkarten.
6. Habits: Pill-Leiste, Monatskopf mit Kennzahlen (Serie im Service), Kalenderzellen mit Zielabstufung, Tages-Sheet, Konfigurationsdialog.
7. Übrige Seiten, Login/Offline/Wartung nur Tokens.
8. Abnahme nach Abschnitt 8, Ergebnisdokument, Werkbaum `[x]`, README.
## 8. Abnahme
- `./gradlew test -Pproduction=true` und `./gradlew firefoxAcceptance` (mit `JAVA_HOME` auf das gepinnte JDK) ohne Fehler; Anzahl Szenarien im Ergebnisdokument.
- `tools/test-frontend` (Frontend-JavaScript-Tests) ohne Fehler.
- Screenshots aus dem laufenden Produktions-JAR bei 360 × 780, 390 × 844 und 1280 × 900, jeweils hell und dunkel: Today, Arbeit mit geöffnetem Sheet, Habits-Kalender, Login. Testdaten dafür: vier Arbeitseinträge über drei Kunden am selben Tag und drei aktive Habits mit Grenzen. Kein horizontales Scrollen; alle Texte lesbar; Bottom-Nav verdeckt keinen Inhalt; Today bei 390 × 844 vollständig ohne Scrollen sichtbar.
- Manuelle Prüfung im Chromium-Gerätemodus: Erfassen über Today → Sheet → Speichern → zurück auf Today; Habit hochzählen; Kalenderzelle öffnen und zählen; Theme in Einstellungen umschalten; Sprache umschalten.
- Kontrastwerte der in 4.6 genannten Kombinationen im Ergebnisdokument.
- Keine Fehler in der Browserkonsole während der manuellen Prüfung.