docs: T5 Bewegungs-Sprache (Spec §17) und Plan-Review 2026-10-16
Spec: Zweck, Stufen Voll/Dezent/Aus über useEffectiveMotionLevel, Tokens aus dem, was Beute und CSS heute nutzen (eine Quelle in lib/motion/tokens.ts), Regeln je Muster, was nie animiert, Performance-Regeln, Abnahme; neue Inhaber-Frage 4. Plan: 22 Korrekturen gegen den aktuellen Code (u. a. 31 statt 17 Checkbox-Stellen, fehlende AREAS, Lebenslinie unter der Beute-Karte, Fokusring auf der Holo-Bühne), neue Tasks A11 (Bewegungs-Sprache durchsetzen) und A12 (Beute-Bühne und Sonne-Modus). Refs #26 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
362ae7466b
commit
1806255211
2 files changed
+394
-8
No files matched your search
@@ -1,6 +1,103 @@
|
|||||||
# T5 Optik mit eigener Handschrift Implementation Plan
|
# T5 Optik mit eigener Handschrift Implementation Plan
|
||||||
|
|
||||||
**Status (2026-10-16):** Noch nicht begonnen. Vorher Plan-Review und die Bewegungs-Sprache aus Beute-Spec §12.4 ergänzen; Inhaber-Fragen der Spec §16 vorläufig mit A/A/A. Issue: [#26](https://git.encry.de/Nick.Rosenloecher/ApiCore/issues/26).
|
**Status (2026-10-16):** Noch nicht begonnen. Plan-Review erledigt (Abschnitt „Review 2026-10-16“ direkt unten), Bewegungs-Sprache steht in Spec §17 und ist als Task A11 eingeplant. Offen beim Inhaber: Spec §16 Fragen 1–3 (vorläufig A/A/A) und die neue Frage 4 (Bewegungs-Sprache, Empfehlung A). Issue: [#26](https://git.encry.de/Nick.Rosenloecher/ApiCore/issues/26).
|
||||||
|
|
||||||
|
## Review 2026-10-16
|
||||||
|
|
||||||
|
Geprüft gegen den Code nach Beute L1–L5, T4a/T4b, T6 und T7 L1/L2/L3/L5. Die Korrekturen gelten vor dem jeweiligen Task-Text. Wo sie ihm widersprechen, gilt die Korrektur.
|
||||||
|
|
||||||
|
**Allgemein**
|
||||||
|
- G1 (Voraussetzungen): T4a **und T4b** sind gemergt (TASKS.md). T5b läuft damit ohne Kalender-Rückfall.
|
||||||
|
- `PHASE_SOURCE_LABELS` liegt in `src/lib/phenology/labels.ts`; die lokale Ersatzdefinition entfällt.
|
||||||
|
- `SeasonChip` liegt in `src/components/autopilot/season-chip.tsx`, `fetchAutopilotToday` in `src/lib/autopilot/api.ts`, `INTENT_ICONS` in `src/components/autopilot/next-steps-card.tsx`.
|
||||||
|
- G2 (Global Constraints, Zeile „Bewegung“): ersetzt durch Spec §17. Ab sofort gilt: `motion-full:` statt `motion-safe:` für jede Verschiebung, Tokens aus `src/lib/motion/tokens.ts`, nichts Datenhaftes bewegt sich. Rad, Lebenslinie und Waben bleiben ohne Animation.
|
||||||
|
- G3 (Bestand `motion`): Installiert ist `motion` **13.4.6**, nicht 12.x. `maath` ist da, `framer-motion` nur transitiv. Kein Quelltext importiert `motion/react` (Beute §9.8 sah es für die DOM-Teile vor, gebaut wurde CSS). Task A11 bringt die ersten DOM-Nutzer. Ohne A11 wäre die Abhängigkeit ungenutzt.
|
||||||
|
- G4 (Aufwand): T5a wächst von ≈ 4,75 auf ≈ 6,4 PT. Gründe: A7 +0,4 (31 statt 17 Checkbox-Stellen), neue Tasks A11 (0,75) und A12 (0,5). T5b bleibt bei ≈ 5,25 PT.
|
||||||
|
- G5 (Wellen T5a): A11 läuft in Welle 9 nach A8. Sie teilt `globals.css` mit A1/A4/A5, `today-panel.tsx` mit A7 und `ui/*` mit A5. A12 läuft in Welle 9 parallel; sie fasst nur `components/hive/**` an, `globals.css` nicht, weil die Holo-Sonne-Werte schon in A1 kommen. A10 rückt in Welle 10 und wartet auf A8, A9, A11 und A12.
|
||||||
|
|
||||||
|
**Je Task**
|
||||||
|
- A1:
|
||||||
|
- `--holo-*` (in `:root`/`.dark` seit Beute L3) bekommt Werte in beiden Sonne-Sätzen: Bühne `oklch(0 0 0)`, Kanten und Etikett-Text `oklch(1 0 0)`, Etikett-Fläche `oklch(0.12 0 0)`.
|
||||||
|
- Neu ist der Rahmen `.holo-stage { --ring: var(--holo-edge-strong) }` (Spec §17.8).
|
||||||
|
- `contrast.spec.ts` bekommt zwei Zeilen: `holo-label-fg`/`holo-label-bg` ≥ 7 und `holo-edge-strong`/`holo-bg` ≥ 3, in allen vier Sätzen.
|
||||||
|
- Grund: `--primary-ink` auf `--holo-bg` ≈ 2,8:1.
|
||||||
|
- A2:
|
||||||
|
- `motion-preference.ts` hat seit der Beute `useEffectiveMotionLevel` und `MOTION_EVENT`. Nutzer sind `hive-canvas.tsx`, `hive-visual-slot.tsx` und `today/use-task-actions.ts`. Die API bleibt; `useAppearance().setMotion` löst `MOTION_EVENT` aus.
|
||||||
|
- Neu ist das Ereignis `apicore:appearance` bei jedem Wechsel von Schema oder Kontrast; A12 hört darauf.
|
||||||
|
- Test dazu: `it('a motion change from the appearance section reaches useEffectiveMotionLevel in the same tab', ...)`.
|
||||||
|
- Die Stufe heißt in der Oberfläche „Dezent“.
|
||||||
|
- A3:
|
||||||
|
- Verlauf-Verbraucher sind **33 Dateien**, nicht 31. `text-white` steht zusätzlich in `components/hive/editor/editor-stage-overlay.tsx` (auf der Bühne → `text-holo-label-fg`).
|
||||||
|
- `AREAS` fehlen Bereiche aus T6/T7/Beute, die in `nav-config.ts`/`quick-actions.ts` stehen. Neue Schlüssel:
|
||||||
|
- `compliance` (`ShieldCheck`, betrieb)
|
||||||
|
- `medicines` (`Pill`, erfassung)
|
||||||
|
- `voice` (`Mic`, erfassung)
|
||||||
|
- `hive-systems` (`Box`, neutral)
|
||||||
|
- `system` (`Activity`, neutral)
|
||||||
|
- `areas.spec` prüft danach wieder jeden Nav- und Quick-Action-Eintrag.
|
||||||
|
- Neue Icon-Abweichungen, die korrigiert werden müssen:
|
||||||
|
- Arzneimittel-Packungen nutzen `Package` (= Abfüllungen) in `medicines/[id]/page.tsx` und `medicines/lots/[id]/page.tsx`.
|
||||||
|
- Beutensysteme nutzen `Boxes` (= Rohstoffe) in `settings/hive-systems/page.tsx`, die Navigation dagegen `Box`.
|
||||||
|
- `settings/voice/page.tsx` nutzt `ShieldCheck` (= Prüfbereitschaft).
|
||||||
|
- Das Dashboard trägt seit T6 `ComplianceLine` und `StandNote`; beide bleiben unverändert.
|
||||||
|
- A4:
|
||||||
|
- Die Codemod-Trefferzahlen vor dem Lauf neu zählen (`grep`), die Zahlen im Task sind vom 14.10.
|
||||||
|
- `dark:` steht weiter in genau 8 `ui/*`-Dateien.
|
||||||
|
- Die Beute-Bühne (`bg-holo-*`) bleibt unberührt: Sie ist in hell und dunkel absichtlich dunkel.
|
||||||
|
- A5:
|
||||||
|
- `sm:h-`/`sm:min-h-` stehen in **11 Dateien**, nicht in 4.
|
||||||
|
- Der Fokus-Codemod trifft 5 Stellen in `components/hive/**`. Dort entfällt der eigene Ring, und der Ring kommt aus `.holo-stage` (A1).
|
||||||
|
- `.honey-check` hat `outline: none` plus eigenen Schatten-Ring in `globals.css`. Er nimmt die globale Regel, die Ausnahme entfällt.
|
||||||
|
- A6:
|
||||||
|
- `NumberStepper` hat seit Beute L2 `step` mit Dezimalschritten; die Umstellungen nutzen nur ganze Schritte.
|
||||||
|
- Neue Radio-Eigenbauten → `ChoiceGroup`: `compliance/notices/create-notice-dialog.tsx` (`type="radio"`) und `components/phenology/phenology-report-sheet.tsx` (`type="radio"`).
|
||||||
|
- Neues `aria-pressed` in `components/hive/hive-card.tsx` („Aufbau als Liste“) → `Button pressed`.
|
||||||
|
- Ergänze die Dateien in der Files-Liste und im Testbefehl (`"src/app/(app)/compliance" src/components/phenology src/components/hive`).
|
||||||
|
- A7: Heute sind es **31 Stellen in 27 Dateien** (13 `ToggleChip`, 13 native, 4 Base UI, 1 `today-panel`).
|
||||||
|
- `ToggleChip`: 13 Stellen in 11 Dateien, neu dazugekommen:
|
||||||
|
- `compliance/notices/create-notice-dialog.tsx`
|
||||||
|
- `compliance/restrictions/create-restriction-dialog.tsx`
|
||||||
|
- `compliance/settings/page.tsx` ×3
|
||||||
|
- `medicines/medicine-form-dialog.tsx`
|
||||||
|
- `profile/telegram-link-card.tsx`
|
||||||
|
- `settings/automation/automation-rules-tab.tsx`
|
||||||
|
- `settings/automation/rule-forms/rule-form-parts.tsx`
|
||||||
|
- Natives `type="checkbox"`, neu: `settings/hive-systems/system-dialog.tsx`, `settings/voice/page.tsx`, `components/colonies/colony-takeover-section.tsx`.
|
||||||
|
- `components/form/switch-row.tsx` (`role="switch"`, Nutzer `profile/notification-settings-card.tsx`, `telegram-link-card.tsx`) geht in `CheckboxField` auf. Dafür bekommt `CheckboxField` das Prop `role?: "checkbox" | "switch"`, Umsetzung mit Base UI `Switch`. Danach wird `SwitchRow` gelöscht. Die Wächter-Regel erlaubt `role="switch"` nur in `ui/checkbox.tsx`/`ui/switch.tsx`.
|
||||||
|
- `today-panel.tsx` (`task`) kommt zuletzt, weil A11 dieselbe Datei danach anfasst.
|
||||||
|
- Aufwand 0,9 PT.
|
||||||
|
- A8:
|
||||||
|
- `ActiveHistoryTabs` hat jetzt **10 Nutzer**, neu ist `medicines/page.tsx`; sie kommt in die Files-Liste.
|
||||||
|
- `TabSwitcher` hat unverändert 2 Nutzer.
|
||||||
|
- A9:
|
||||||
|
- `SHELL_CACHE` ist weiter `apicore-shell-v4` → v5 gilt.
|
||||||
|
- `SW_ROUTES_VERSION` haben Beute L5 und T7 erhöht. „Bleibt unverändert“ heißt: A9 erhöht sie nicht noch einmal.
|
||||||
|
- `/inspections/new`, `/varroa/new`, `/voice/new` stehen in `SHELL_ROUTES`. Ein vierter Shortcut „Sprachnotiz“ (`/voice/new`) ist möglich, aber nicht Spec; nur nach Freigabe.
|
||||||
|
- A10:
|
||||||
|
- `src/app/(app)/dashboard/page.spec.tsx` gibt es schon → Modify, nicht Create.
|
||||||
|
- Die Screenshot-Matrix bekommt die Seiten aus T6, T7 und Beute:
|
||||||
|
- Volk mit Beute-Karte (Bühne, Fokus, Sheet)
|
||||||
|
- `/colonies/[id]/hive` (Umbauen)
|
||||||
|
- `/voice` und `/voice/new` (Aufnahme-Punkt)
|
||||||
|
- `/settings/voice`
|
||||||
|
- `/offline`, `/offline/apiary` und `/offline/colony`
|
||||||
|
- das Warteschlangen-Sheet (`components/offline/pending-queue-sheet.tsx`) und das Offline-Band
|
||||||
|
- `/compliance`, `/medicines`
|
||||||
|
- Die Matrix läuft je in Voll, Dezent und Aus, zusätzlich mit `prefers-reduced-motion: reduce` auf Start, Volk und `/voice/new`.
|
||||||
|
- A11 **neu**: Bewegungs-Sprache durchsetzen (Task-Text unten nach A10).
|
||||||
|
- A12 **neu**: Beute-Bühne an Sonne und Darstellungswechsel anschließen (Task-Text unten nach A11).
|
||||||
|
- B1: Die Offline-Standseite `src/app/(app)/offline/apiary/page.tsx` zeigt die Wabe aus dem Paket mit demselben `ApiaryComb` (Modify ergänzen, Test `it('the offline apiary page shows the comb from the pack', ...)`). `apiary-colonies-card.tsx` nutzt `inspectionDueOn` schon. Die Tests für den Zweig ohne T4 bleiben für alte Pakete.
|
||||||
|
- B3: Die Keys stimmen (`varroa-measurements`, `swarm-control-checks`, `next-steps`, `history`). `["colonies", id, "hive"]` gibt es neu. Beute-Änderungen als Ereignisse sind laut Beute §9.10 nicht Teil von T5; die Lebenslinie lässt sie aus.
|
||||||
|
- B4: Die Volksseite hat seit Beute L2 ein neues Raster:
|
||||||
|
- oben `NextStepsCard` über die volle Breite
|
||||||
|
- Zeile 1: Stammdaten und Beute, gleich hoch, ohne `items-start`
|
||||||
|
- Zeile 2: Aktionen | Karten
|
||||||
|
- „Desktop rechte Spalte oben“ ist jetzt die Beute. Die Lebenslinie kommt in Zeile 2 als erstes Kind der 2-Spalten-Fläche, über dem `xl:grid-cols-2`-Raster.
|
||||||
|
- Reihenfolge im DOM: Stammdaten, Beute, Aktionen, Lebenslinie, Karten.
|
||||||
|
- Test `it('the colony page puts the lifeline under „Als Nächstes“', ...)` → `it('the lifeline opens row 2 above the record cards and leaves row 1 to Stammdaten and Beute', ...)`.
|
||||||
|
- B5: Unverändert gültig. `AutopilotToday.seasons` hat weiter weder `segmentSince`, `reportedOn` und `note` noch `seasonCalendar`. Die Dateien sind `src/autopilot/next-steps.service.ts`, `src/autopilot/dto/next-steps-response.dto.ts` und `test/next-steps.e2e-spec.ts`.
|
||||||
|
- B6: `season-defaults.spec.ts` liest nicht `default-rules.ts`, sondern `ApiCore-Backend/src/automation/rule-config/phenology.config.ts` (`calendarFallback`, Schwarmzeit) und `season-autopilot.config.ts` (`winterVarroaWindow`).
|
||||||
|
- B7: Das Dashboard hat heute noch Honigbanner und `StatTile`; A3 und B7 sind also weiter nötig. `ComplianceLine` und `StandNote` bleiben über „Bienenjahr“ an ihrem Platz.
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
@@ -12,7 +109,7 @@
|
|||||||
|
|
||||||
**Spec:** `docs/superpowers/specs/2026-10-13-t5-optik-design.md` (bindend). Bausteine aus `docs/superpowers/plans/2026-10-13-t3-erfassung-am-stand.md` und `docs/superpowers/plans/2026-10-13-t4-logik-ketten.md`.
|
**Spec:** `docs/superpowers/specs/2026-10-13-t5-optik-design.md` (bindend). Bausteine aus `docs/superpowers/plans/2026-10-13-t3-erfassung-am-stand.md` und `docs/superpowers/plans/2026-10-13-t4-logik-ketten.md`.
|
||||||
|
|
||||||
**Lieferungen:** T5a „Sonnentauglich und Baukasten“ (Tasks A1–A10, ≈ 4,75 PT, hängt nur an T3) und T5b „Handschrift“ (Tasks B1–B8, ≈ 5,25 PT, nach T4a; ohne T4 greift der Kalender-Rückfall).
|
**Lieferungen:** T5a „Sonnentauglich und Baukasten“ (Tasks A1–A12, ≈ 6,4 PT nach Review 2026-10-16, hängt nur an T3) und T5b „Handschrift“ (Tasks B1–B8, ≈ 5,25 PT, nach T4a; ohne T4 greift der Kalender-Rückfall).
|
||||||
|
|
||||||
**Vorläufige Inhaber-Entscheidungen (Empfehlungen, vor der Ausführung noch überstimmbar):** Hauptknopf = heller Honig mit dunkler Tinte (Offene Frage 1, A, 9,4:1). Zellen der Waben-Standkarte nach Volkscode (Frage 2, A). Standard-Farbschema auf neuem Gerät = Hell (Frage 3, A). Kippt eine Antwort, ändert sich nur das Token bzw. eine Konstante (`DEFAULT_THEME` in Task A2, `--primary*` in Task A4, `sortCells` in Task B1); `contrast.spec.ts` hält jede Variante.
|
**Vorläufige Inhaber-Entscheidungen (Empfehlungen, vor der Ausführung noch überstimmbar):** Hauptknopf = heller Honig mit dunkler Tinte (Offene Frage 1, A, 9,4:1). Zellen der Waben-Standkarte nach Volkscode (Frage 2, A). Standard-Farbschema auf neuem Gerät = Hell (Frage 3, A). Kippt eine Antwort, ändert sich nur das Token bzw. eine Konstante (`DEFAULT_THEME` in Task A2, `--primary*` in Task A4, `sortCells` in Task B1); `contrast.spec.ts` hält jede Variante.
|
||||||
|
|
||||||
@@ -33,7 +130,7 @@
|
|||||||
- Grundschrift 16 px (`text-base`) für Fließtext, Labels, Auswahl-Knöpfe, Listentitel, Knopftext, Eingabe; Nebentext 14 px (`text-sm`); `text-xs` = 13 px nur für Achsen, Legenden, Fußzeilen; in Sonne 16/14 px. Überschriften Space Grotesk, nie in Knöpfen oder Labels.
|
- Grundschrift 16 px (`text-base`) für Fließtext, Labels, Auswahl-Knöpfe, Listentitel, Knopftext, Eingabe; Nebentext 14 px (`text-sm`); `text-xs` = 13 px nur für Achsen, Legenden, Fußzeilen; in Sonne 16/14 px. Überschriften Space Grotesk, nie in Knöpfen oder Labels.
|
||||||
- Ein Fokusstil: 2 px durchgehender Ring in `--ring`, 2 px Abstand (Sonne 3 px), als Basisregel auf `:where(:focus-visible)`. Einzige Ausnahme: `honeycomb-cell.tsx` (zweiter Sechseckpfad, Spec 6.6).
|
- Ein Fokusstil: 2 px durchgehender Ring in `--ring`, 2 px Abstand (Sonne 3 px), als Basisregel auf `:where(:focus-visible)`. Einzige Ausnahme: `honeycomb-cell.tsx` (zweiter Sechseckpfad, Spec 6.6).
|
||||||
- Zustand nie nur über Farbe: Icon plus Wort, Rand-Stufen über Strichstärke und Strichelung. Jede Grafik hat `<title>`, `<desc>` und eine aufklappbare Tabelle aus demselben Modell.
|
- Zustand nie nur über Farbe: Icon plus Wort, Rand-Stufen über Strichstärke und Strichelung. Jede Grafik hat `<title>`, `<desc>` und eine aufklappbare Tabelle aus demselben Modell.
|
||||||
- Bewegung: keine neue Animation. Rad, Lebenslinie, Waben erscheinen sofort. Erlaubt bleibt nur die `honey-check`-Bewegung beim Abhaken. `prefers-reduced-motion` und `data-motion` bleiben wirksam.
|
- Bewegung (geändert 2026-10-16): Spec §17 ist bindend. Stufen über `useEffectiveMotionLevel()` und die CSS-Variante `motion-full:` (nie `motion-safe:` für Verschiebung). Tokens kommen aus `src/lib/motion/tokens.ts` und `--motion-*`. Bewegt werden nur `transform` und `opacity`, Datenwerte, Fehlertexte und Eingaben nie. `motion` nur über `LazyMotion`/`m.*` im `(app)`-Layout. Rad, Lebenslinie und Waben erscheinen weiter sofort. Durchgesetzt wird das in Task A11, der Wächter prüft es.
|
||||||
- Deutsche Oberfläche, kurze Sätze, ein Hauptknopf (Honig) je Ansicht, ADHS-freundlich: wenig kognitive Last. Code-Bezeichner englisch.
|
- Deutsche Oberfläche, kurze Sätze, ein Hauptknopf (Honig) je Ansicht, ADHS-freundlich: wenig kognitive Last. Code-Bezeichner englisch.
|
||||||
- Verboten (Wächter prüft): Weiß auf Honig, `text-primary` als Schriftfarbe (nur `text-primary-ink`), `ring-ring/50`, `outline-none`, Farbverlauf als Hintergrund, `text-white`, `dark:`-Klassen, Text < 13 px, Zustand nur über Farbe, eigene `role="radio|checkbox|tab"`-Bauten außerhalb des Baukastens. Ein Bereichston ist nie eine Zustandsfarbe.
|
- Verboten (Wächter prüft): Weiß auf Honig, `text-primary` als Schriftfarbe (nur `text-primary-ink`), `ring-ring/50`, `outline-none`, Farbverlauf als Hintergrund, `text-white`, `dark:`-Klassen, Text < 13 px, Zustand nur über Farbe, eigene `role="radio|checkbox|tab"`-Bauten außerhalb des Baukastens. Ein Bereichston ist nie eine Zustandsfarbe.
|
||||||
- Wiederverwenden statt kopieren: `StatusLightBadge`, `colonyStatusLight`, `ChoiceGroup`, `NumberStepper`, `Field`, `QueryState`, `PageHeader`/`DetailHeader`/`EntityCard`, `useColonyOverview`, Query-Keys der bestehenden Karten (keine Doppel-Requests).
|
- Wiederverwenden statt kopieren: `StatusLightBadge`, `colonyStatusLight`, `ChoiceGroup`, `NumberStepper`, `Field`, `QueryState`, `PageHeader`/`DetailHeader`/`EntityCard`, `useColonyOverview`, Query-Keys der bestehenden Karten (keine Doppel-Requests).
|
||||||
@@ -165,7 +262,8 @@ Die Honigfläche hat gegen die Karte nur 1,87:1; das ist zulässig, weil ein Kno
|
|||||||
| 6 | A6 | A5 |
|
| 6 | A6 | A5 |
|
||||||
| 7 | A7 | A6 (`winter-check-dialog`, `label-designer`, `swarm-control-edit-dialog` gemeinsam) |
|
| 7 | A7 | A6 (`winter-check-dialog`, `label-designer`, `swarm-control-edit-dialog` gemeinsam) |
|
||||||
| 8 | A8 | A7 (`tasks/page.tsx`, `settings/automation/*` gemeinsam) |
|
| 8 | A8 | A7 (`tasks/page.tsx`, `settings/automation/*` gemeinsam) |
|
||||||
| 9 | A10 | A8, A9 |
|
| 9 | A11, A12 | A11 → A8 (`today-panel.tsx`, `ui/*`, `globals.css`); A12 → A1, A2 |
|
||||||
|
| 10 | A10 | A8, A9, A11, A12 |
|
||||||
|
|
||||||
**T5b** (nach T4a-Frontend; ohne T4 mit Kalender-Rückfall lauffähig, aber dieselben Dateien wie T4 Tasks 17/18/20, darum danach):
|
**T5b** (nach T4a-Frontend; ohne T4 mit Kalender-Rückfall lauffähig, aber dieselben Dateien wie T4 Tasks 17/18/20, darum danach):
|
||||||
|
|
||||||
@@ -576,6 +674,135 @@ it('the dashboard shows no white text on honey', ...);
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Task A11: Bewegungs-Sprache durchsetzen (Spec §17, neu im Review 2026-10-16)
|
||||||
|
|
||||||
|
Vorher laden:
|
||||||
|
- `animate` bzw. `ecc:motion-foundations` für Konzept und Tokens
|
||||||
|
- `motion` (Docs „Reduce bundle size“, `LazyMotion`, `layout`, `AnimatePresence`)
|
||||||
|
- danach `fixing-motion-performance`
|
||||||
|
|
||||||
|
Skill-Routing: erst Konzept, dann CSS, dann `motion`. GSAP nicht.
|
||||||
|
|
||||||
|
**Files**
|
||||||
|
- Create:
|
||||||
|
- `src/lib/motion/tokens.ts` (+ `tokens.spec.ts`)
|
||||||
|
- `src/components/app-motion.tsx` (+ Spec): `LazyMotion` mit `strict`, Features asynchron, `MotionConfig` je Stufe
|
||||||
|
- `src/lib/motion/motion-features.ts`: `export default domMax`, nur per dynamischem Import
|
||||||
|
- `scripts/codemod-t5-motion.mjs` (+ Spec)
|
||||||
|
- Modify:
|
||||||
|
- `src/app/globals.css`:
|
||||||
|
- `--motion-fade`, `--motion-enter`, `--motion-settle`, `--motion-moment`, `--ease-in`, `--ease-spring`, `--motion-shift`
|
||||||
|
- `--animate-page-in` auf `--motion-enter`
|
||||||
|
- `next-step-in` ohne `filter: blur`
|
||||||
|
- `.today-row`-Faltung entfernen
|
||||||
|
- `src/app/(app)/layout.tsx` (`<AppMotion>` um den Inhalt)
|
||||||
|
- `src/app/(app)/page-transition.tsx`
|
||||||
|
- `src/components/ui/sheet.tsx`, `dialog.tsx`, `dropdown-menu.tsx`, `select.tsx` (Dauern und Kurven aus Tokens, Austritt `--ease-in`)
|
||||||
|
- `src/components/ui/button.tsx` (`motion-full:active:scale-[0.97]`, kein `transition-all`)
|
||||||
|
- `src/components/ui/badge.tsx`, `card.tsx`, `input.tsx`, `textarea.tsx`, `tabs.tsx` (`transition-all` raus; Karte ohne Hover-Heben)
|
||||||
|
- `src/app/(app)/sidebar.tsx` (ohne `group-hover:scale-110`, ohne `transition-all`)
|
||||||
|
- `src/components/stat-bar-list.tsx` (Balken ohne Übergang)
|
||||||
|
- `src/components/today/today-panel.tsx` und `today/use-task-actions.ts`: Wegfalten über `AnimatePresence` und `m.li layout` mit `SPRING_FLIGHT`; `EXIT_DELAY_MS` bleibt
|
||||||
|
- `src/components/offline/pending-queue-sheet.tsx` und `src/app/(app)/voice/page.tsx`: Entfernen über dasselbe Muster
|
||||||
|
- alle `motion-safe:`-Treffer mit Verschiebung, Drehung, `animate-pulse` oder `animate-spin` (Codemod → `motion-full:`). Heute betroffen:
|
||||||
|
- `rule-card-shell.tsx`, `suggestions/page.tsx`, `restriction-override.tsx`, `form-section.tsx`, `suggestion-group.tsx`
|
||||||
|
- `hive-card.tsx`, `colonies/[id]/hive/page.tsx`, `voice-capture-card.tsx`, `voice-recorder.tsx`
|
||||||
|
- `label-compliance-panel.tsx` (`motion-reduce:animate-none` → `motion-full:animate-spin`)
|
||||||
|
- Beute als Verbraucher derselben Quelle:
|
||||||
|
- `src/components/hive/hive-scene/quality.ts` (`SPRING_STIFFNESS`/`SPRING_DAMPING` aus `tokens.ts`)
|
||||||
|
- `use-hive-motion.ts` (`SUBTLE_FADE_SECONDS` aus `tokens.ts`)
|
||||||
|
- `hive-visual-slot.tsx` (`FADE_MS.subtle` = `--motion-fade`)
|
||||||
|
- `src/styles/baukasten-guard.spec.ts`: Regelgruppe „Bewegung“ (Spec §17.7)
|
||||||
|
- Unverändert: three.js-Szene (`use-animated-node.ts`, `model-rig.tsx`); `.honey-check` (nur Fokus-Ausnahme aus A5).
|
||||||
|
|
||||||
|
**Interfaces**
|
||||||
|
```ts
|
||||||
|
export const MOTION = { fadeMs: 150, enterMs: 200, settleMs: 350, momentMs: 420, shiftPx: 8 } as const;
|
||||||
|
export const SPRING_FLIGHT = { type: "spring", stiffness: 210, damping: 21, mass: 1 } as const; // = Beute-Feder
|
||||||
|
export const SUBTLE_FADE_SECONDS: number; // MOTION.fadeMs / 1000
|
||||||
|
export function springEasing(points?: number): string; // CSS linear(...) aus springStep; erzeugt --ease-spring
|
||||||
|
export function transitionFor(level: MotionLevel, kind: "enter" | "exit" | "fade" | "layout"): Transition; // off → { duration: 0 }
|
||||||
|
export function scrollBehaviorFor(level: MotionLevel, distancePx: number, viewportPx: number): ScrollBehavior; // smooth nur bei full und < 1 Bildschirm
|
||||||
|
export function AppMotion(props: { children: ReactNode }): JSX.Element; // full: reducedMotion "never"; subtle: "always"; off: transition duration 0
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Schritt 1: Failing Tests**
|
||||||
|
```ts
|
||||||
|
// tokens.spec.ts
|
||||||
|
it('css motion variables equal the TS tokens', ...); // liest globals.css
|
||||||
|
it('the flight spring equals the hive spring in quality.ts', ...);
|
||||||
|
it('--ease-spring samples stay within 0.02 of springStep', ...);
|
||||||
|
it('transitionFor off has zero duration and subtle has no spatial part', ...);
|
||||||
|
it('scrollBehaviorFor is smooth only at full and below one viewport', ...);
|
||||||
|
// app-motion.spec.tsx
|
||||||
|
it('sets reducedMotion per level and follows a level change in the same tab', ...);
|
||||||
|
// today-panel.spec.tsx (neu, gibt es noch nicht)
|
||||||
|
it('a ticked task leaves via layout at full and instantly at off', ...);
|
||||||
|
it('a ticked task never collapses grid-template-rows', ...);
|
||||||
|
// guard (baukasten-guard.spec.ts, Gruppe Bewegung)
|
||||||
|
it('no motion-safe: on translate, scale, rotate, slide, zoom or animate classes', ...);
|
||||||
|
it('no transition-all and no duration above 420 ms outside components/hive', ...);
|
||||||
|
it('hover translate or scale only inside components/hive', ...);
|
||||||
|
it('motion/react is used only for LazyMotion, MotionConfig, AnimatePresence and hooks; elements come from motion/react-m', ...);
|
||||||
|
it('globals.css keyframes and transitions do not animate width, height, grid-template-rows, filter or box-shadow', ...);
|
||||||
|
it('stat bars and other data values have no transition', ...);
|
||||||
|
```
|
||||||
|
- [ ] **Schritt 2:** `npx vitest run src/lib/motion src/components/app-motion.spec.tsx src/components/today src/styles scripts` → FAIL.
|
||||||
|
- [ ] **Schritt 3: Implementieren.**
|
||||||
|
- Codemod (`motion-safe:` → `motion-full:` nur für Verschiebung, Pulsieren und Drehen; Farbübergänge bleiben ohne Variante) ausführen und den Diff prüfen.
|
||||||
|
- Dann die Tokens setzen, `AppMotion` einbauen und die Listen umstellen.
|
||||||
|
- Skelett-Verzögerung 150 ms und Spinner ab 300 ms mit Wort, wo `QueryState`/Knöpfe das heute nicht tun (Spec §17.4).
|
||||||
|
- Toast bei „Dezent“ ohne Gleiten (Sichtprüfung; falls nötig CSS an `[data-sonner-toaster]`).
|
||||||
|
- [ ] **Schritt 4:** → PASS.
|
||||||
|
- [ ] **Schritt 5: Verifikation** – V mit `src scripts`; Bundle-Messung `motion`-Chunk ≤ 30 kB gzip, nicht im First Load von Login und Herkunft (Build-Ausgabe).
|
||||||
|
- VB:
|
||||||
|
- jedes Muster aus Spec §17.4 auf Start (Abhaken), Aufgaben, Warteschlangen-Sheet, Volk (Sheet, Beute), `/voice` (Entfernen), `/voice/new` (Aufnahme-Punkt)
|
||||||
|
- je Voll, Dezent, Aus und `prefers-reduced-motion: reduce`, 390 px und 1366 px
|
||||||
|
- Performance-Trace bei 4-facher CPU-Drosselung (390 px):
|
||||||
|
- Sheet auf/zu, Abhaken mit Wegfalten, Seitenwechsel
|
||||||
|
- kein Frame > 32 ms, kein Layout-Eintrag während der Bewegung außer der `layout`-Messung
|
||||||
|
- Danach `review-animations` über den Diff.
|
||||||
|
- Kein Commit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task A12: Beute-Bühne an Sonne und Darstellungswechsel anschließen (Spec §17.8, Beute-Spec §9.3, neu im Review 2026-10-16)
|
||||||
|
|
||||||
|
**Files**
|
||||||
|
- Modify:
|
||||||
|
- `src/components/hive/hive-scene/theme.ts` (Tokens bei `apicore:appearance` neu lesen statt einmalig)
|
||||||
|
- `hive-canvas.tsx` (Bloom aus in Sonne)
|
||||||
|
- `materials.ts` und `frame-textures.ts` (Sonne: Kanten 2 px reinweiß; Rähmchen-Arten als Schraffur je Art statt Tönung)
|
||||||
|
- `hive-labels.tsx` und `src/components/hive/hive-glass.ts` (Sonne: Etiketten 16 px, kein `backdrop-blur`)
|
||||||
|
- `src/components/hive/hive-card.tsx` und `src/app/(app)/colonies/[id]/hive/page.tsx` (Bühnen-Wurzel bekommt `.holo-stage`)
|
||||||
|
- Specs dazu (`hive-scene-content.spec.tsx`, `frame-textures.spec.ts`)
|
||||||
|
- Consumes: Holo-Sonne-Tokens und `.holo-stage` (A1), Ereignis `apicore:appearance` und `useAppearance().sunActive` (A2).
|
||||||
|
|
||||||
|
**Interfaces**
|
||||||
|
- `readHoloTheme(): HoloTheme` liest bei jedem Aufruf neu. `useHoloTheme()` abonniert `apicore:appearance` und ruft `invalidate()` auf.
|
||||||
|
- `frameLookFor(type, { sun }): { color: Color | null; hatch: HatchKind | null }`.
|
||||||
|
|
||||||
|
- [ ] **Schritt 1: Failing Tests**
|
||||||
|
```ts
|
||||||
|
it('the scene re-reads the holo tokens when the appearance changes', ...);
|
||||||
|
it('sun turns bloom off and draws white edges', ...);
|
||||||
|
it('in sun every frame type has its own hatch and no tint', ...);
|
||||||
|
it('labels are 16 px and without backdrop blur in sun', ...);
|
||||||
|
it('focus inside the stage uses the holo ring token', ...); // .holo-stage setzt --ring
|
||||||
|
```
|
||||||
|
- [ ] **Schritt 2:** `npx vitest run src/components/hive` → FAIL.
|
||||||
|
- [ ] **Schritt 3: Implementieren.**
|
||||||
|
- [ ] **Schritt 4:** → PASS.
|
||||||
|
- [ ] **Schritt 5: Verifikation** – V mit `src/components/hive`. VB:
|
||||||
|
- Volk und `/colonies/[id]/hive` in hell, dunkel, Sonne hell und Sonne dunkel
|
||||||
|
- Sonne live umschalten: Die Szene zieht ohne Neuladen mit
|
||||||
|
- Tastatur-Fokus auf Glas-Knöpfen sichtbar
|
||||||
|
- Budget des 3D-Chunks unverändert (CI-Prüfung)
|
||||||
|
|
||||||
|
Kein Commit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Lieferung T5b – Handschrift
|
## Lieferung T5b – Handschrift
|
||||||
|
|
||||||
### Task B1: Waben-Standkarte (Spec 6)
|
### Task B1: Waben-Standkarte (Spec 6)
|
||||||
@@ -918,7 +1145,8 @@ it('the pointer is a fixed element without animation', ...);
|
|||||||
| 13.4 Sichtprüfung, Lighthouse, Impeccable, Draußen-Test | A10, B8 |
|
| 13.4 Sichtprüfung, Lighthouse, Impeccable, Draußen-Test | A10, B8 |
|
||||||
| 14 Aufwand, Zwei Lieferungen | Kopf, Abhängigkeiten |
|
| 14 Aufwand, Zwei Lieferungen | Kopf, Abhängigkeiten |
|
||||||
| 15 Rollout (Reihenfolge, Service Worker, Nutzerhinweis, Doku) | Abhängigkeiten, A9, A10, B8 |
|
| 15 Rollout (Reihenfolge, Service Worker, Nutzerhinweis, Doku) | Abhängigkeiten, A9, A10, B8 |
|
||||||
| 16 Offene Fragen (vorläufig A/A/A) | Kopf |
|
| 16 Offene Fragen (vorläufig A/A/A, Frage 4 Bewegungs-Sprache neu, Empfehlung A) | Kopf, Status |
|
||||||
|
| 17 Bewegungs-Sprache (Stufen, Tokens, Muster, Nie-Liste, Leistung, Abnahme) | A11; Beute-Bühne 17.8: A1, A12 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# T5 Optik mit eigener Handschrift — Design
|
# T5 Optik mit eigener Handschrift — Design
|
||||||
|
|
||||||
Stand: 2026-10-14 · Betrifft: ApiCore-Frontend (plus ein kleiner Zusatz am Backend-Endpunkt `GET /autopilot/today`) · Teilprojekt T5 aus `docs/superpowers/specs/2026-10-12-plattform-rueckgrat-und-fahrplan-design.md` §10
|
Stand: 2026-10-14, Nachtrag 2026-10-16 (§17 Bewegungs-Sprache, Frage 4 in §16; Anlass Beute-Spec §12.4, Issue #26) · Betrifft: ApiCore-Frontend (plus ein kleiner Zusatz am Backend-Endpunkt `GET /autopilot/today`) · Teilprojekt T5 aus `docs/superpowers/specs/2026-10-12-plattform-rueckgrat-und-fahrplan-design.md` §10
|
||||||
|
|
||||||
## 1. Ziel
|
## 1. Ziel
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ Technisch heißt das:
|
|||||||
| Randstärke der Zelle | Wie im Fahrplan zeigt die Randstärke die Tage seit der letzten Durchsicht, aber in **drei Stufen** statt stufenlos: dünn = frisch, mittel = bald fällig, dick und gestrichelt = fällig oder nie durchgesehen. Stufenlose Strichstärken sind draußen nicht unterscheidbar. Die genaue Zahl steht im Namen der Zelle und in der Liste. |
|
| Randstärke der Zelle | Wie im Fahrplan zeigt die Randstärke die Tage seit der letzten Durchsicht, aber in **drei Stufen** statt stufenlos: dünn = frisch, mittel = bald fällig, dick und gestrichelt = fällig oder nie durchgesehen. Stufenlose Strichstärken sind draußen nicht unterscheidbar. Die genaue Zahl steht im Namen der Zelle und in der Liste. |
|
||||||
| Lebenslinie und Rad: Daten | Nur vorhandene Endpunkte (Volk, Durchsichten, Varroa, Behandlungen, Fütterungen, Schwarmkontrollen, Historie, Königin; ab T4 `next-steps` und Varroa-Bewertung). Einzige Backend-Änderung: `GET /autopilot/today` bekommt **additiv** den Saisonkalender und `segmentSince` je Stand (9.1). |
|
| Lebenslinie und Rad: Daten | Nur vorhandene Endpunkte (Volk, Durchsichten, Varroa, Behandlungen, Fütterungen, Schwarmkontrollen, Historie, Königin; ab T4 `next-steps` und Varroa-Bewertung). Einzige Backend-Änderung: `GET /autopilot/today` bekommt **additiv** den Saisonkalender und `segmentSince` je Stand (9.1). |
|
||||||
| Diagramm-Bibliothek | Keine. Handgeschriebenes SVG wie `colony-varroa.tsx`. Beim Bau den `dataviz`-Skill laden (wie T3/T4). |
|
| Diagramm-Bibliothek | Keine. Handgeschriebenes SVG wie `colony-varroa.tsx`. Beim Bau den `dataviz`-Skill laden (wie T3/T4). |
|
||||||
| Bewegung | Keine neue Animation. Rad, Lebenslinie und Waben erscheinen sofort. Bewegung gibt es nur, wo sie heute einen Zustand zeigt (Abhaken, nächster Schritt). |
|
| Bewegung | **Geändert 2026-10-16, siehe §17.** Eine Bewegungs-Sprache für die ganze App, abgeleitet aus der Beute: Bewegung nur, wo sie Orientierung gibt (woher kommt das Sheet, was ist weg, was hat geklappt), mit wenigen Tokens und den drei Stufen „Voll“, „Dezent“, „Aus“. Rad, Lebenslinie und Waben bleiben Daten und erscheinen weiter sofort (6.8, 8.5, 9.5). `motion` ist seit der Beute installiert und wird nur dort genutzt, wo CSS es nicht kann. |
|
||||||
| Icons | Lucide wie bisher. Eine Tabelle `AREAS` (Abschnitt 11) ist die einzige Quelle für Icon und Farbton je Bereich. Navigation, Kopfzeilen, „+ Neu“ und T4-Vorschläge lesen daraus. |
|
| Icons | Lucide wie bisher. Eine Tabelle `AREAS` (Abschnitt 11) ist die einzige Quelle für Icon und Farbton je Bereich. Navigation, Kopfzeilen, „+ Neu“ und T4-Vorschläge lesen daraus. |
|
||||||
| App-Icons | Ein Master-SVG, daraus erzeugte PNGs (192/512 „any“, 192/512 „maskable“, 512 „monochrome“, 180 Apple, Favicon). Die erzeugten Dateien liegen im Repo; das Skript nutzt `sharp` als neue devDependency. |
|
| App-Icons | Ein Master-SVG, daraus erzeugte PNGs (192/512 „any“, 192/512 „maskable“, 512 „monochrome“, 180 Apple, Favicon). Die erzeugten Dateien liegen im Repo; das Skript nutzt `sharp` als neue devDependency. |
|
||||||
|
|
||||||
@@ -68,6 +68,7 @@ Technisch heißt das:
|
|||||||
| Je ein Baustein für Auswahlgruppe, Checkbox, Tabs, Plus/Minus | `ChoiceGroup`, `Checkbox`/`CheckboxField`, `Tabs`, `NumberStepper` | 10 |
|
| Je ein Baustein für Auswahlgruppe, Checkbox, Tabs, Plus/Minus | `ChoiceGroup`, `Checkbox`/`CheckboxField`, `Tabs`, `NumberStepper` | 10 |
|
||||||
| Feste Icon- und Farbzuordnung pro Bereich | `AREAS`, `AreaIcon` | 11 |
|
| Feste Icon- und Farbzuordnung pro Bereich | `AREAS`, `AreaIcon` | 11 |
|
||||||
| Fokusstil für alle eigenen Bedienelemente | globale Fokusregel | 4.6, 10.6 |
|
| Fokusstil für alle eigenen Bedienelemente | globale Fokusregel | 4.6, 10.6 |
|
||||||
|
| Nachtrag 2026-10-16: eine Bewegungs-Sprache für die App, passend zur Beute (Beute-Spec §12.4) | Stufen, Tokens, Regeln je Muster, Wächter | 17 |
|
||||||
| §11 T5-Befunde: Weiß auf Honig 2,3:1, Eingaberahmen 1,56:1, Dark Mode unerreichbar, Auswahlgruppen von Hand, Checkboxen in 4, Tabs in 3 Varianten | alle behoben | 4.4, 5, 10 |
|
| §11 T5-Befunde: Weiß auf Honig 2,3:1, Eingaberahmen 1,56:1, Dark Mode unerreichbar, Auswahlgruppen von Hand, Checkboxen in 4, Tabs in 3 Varianten | alle behoben | 4.4, 5, 10 |
|
||||||
|
|
||||||
### 2.2 Befunde aus dem Code, die T5 zusätzlich behebt
|
### 2.2 Befunde aus dem Code, die T5 zusätzlich behebt
|
||||||
@@ -129,7 +130,7 @@ Gemessen mit derselben OKLCH-Rechnung wie `src/lib/forms/rating-contrast.spec.ts
|
|||||||
- Rad: Wo stehen wir im Bienenjahr, was kommt?
|
- Rad: Wo stehen wir im Bienenjahr, was kommt?
|
||||||
4. **Nie nur Farbe.** Jeder Zustand hat ein Icon und ein Wort, jede Grafik einen Text und eine Tabelle. Die Zellen tragen den Volkscode und ein Zustands-Icon. Gestrichelte Ränder und Schraffuren unterscheiden auch ohne Farbsehen.
|
4. **Nie nur Farbe.** Jeder Zustand hat ein Icon und ein Wort, jede Grafik einen Text und eine Tabelle. Die Zellen tragen den Volkscode und ein Zustands-Icon. Gestrichelte Ränder und Schraffuren unterscheiden auch ohne Farbsehen.
|
||||||
5. **Vertraute Bedienung, eigene Handschrift im Detail.** Knöpfe, Felder, Tabs und Sheets verhalten sich wie überall im Web. Die Handschrift sitzt in Material (Wachs und Honig), Tinte und dem einen Motiv Wabenzelle, nicht in erfundenen Bedienelementen.
|
5. **Vertraute Bedienung, eigene Handschrift im Detail.** Knöpfe, Felder, Tabs und Sheets verhalten sich wie überall im Web. Die Handschrift sitzt in Material (Wachs und Honig), Tinte und dem einen Motiv Wabenzelle, nicht in erfundenen Bedienelementen.
|
||||||
6. **Ruhe.** Keine Verläufe, kein Glas, keine Zierbewegung, keine Großbuchstaben-Laufschrift. Schatten sind leise und entfallen in Sonne. Die Farbtöne der Bereiche erscheinen nur als Icon-Kachel, nicht als Fläche.
|
6. **Ruhe.** Keine Verläufe, kein Glas, keine Zierbewegung, keine Großbuchstaben-Laufschrift. Schatten sind leise und entfallen in Sonne. Die Farbtöne der Bereiche erscheinen nur als Icon-Kachel, nicht als Fläche. Bewegung folgt §17. Glas, Leuchten und Ruhebewegung gibt es nur auf der Beute-Bühne (§17.8).
|
||||||
|
|
||||||
**Verboten** (der Wächter-Test prüft, was sich prüfen lässt):
|
**Verboten** (der Wächter-Test prüft, was sich prüfen lässt):
|
||||||
- Weiß auf Honig
|
- Weiß auf Honig
|
||||||
@@ -139,6 +140,7 @@ Gemessen mit derselben OKLCH-Rechnung wie `src/lib/forms/rating-contrast.spec.ts
|
|||||||
- Text unter 13 px
|
- Text unter 13 px
|
||||||
- Zustand nur über Farbe
|
- Zustand nur über Farbe
|
||||||
- eigene `role="radio|checkbox|tab"`-Bauten außerhalb des Baukastens
|
- eigene `role="radio|checkbox|tab"`-Bauten außerhalb des Baukastens
|
||||||
|
- Bewegung gegen §17: `motion-safe:` für Verschiebung, `transition-all`, animierte Breite, Höhe oder Unschärfe, bewegte Datenwerte
|
||||||
|
|
||||||
## 4. Tokens
|
## 4. Tokens
|
||||||
|
|
||||||
@@ -1164,3 +1166,159 @@ Der Fahrplan nannte etwa 5 PT. Mehr wird es durch vier Punkte, die §10 nicht ei
|
|||||||
- A: Hell, Dunkel nur auf Wunsch.
|
- A: Hell, Dunkel nur auf Wunsch.
|
||||||
- B: Wie das Gerät. Ein Handy im Dunkelmodus zeigt die App dann auch draußen dunkel.
|
- B: Wie das Gerät. Ein Handy im Dunkelmodus zeigt die App dann auch draußen dunkel.
|
||||||
- **Empfehlung: A.** Draußen bei Tag ist hell besser lesbar. Wer abends dunkel will, stellt einmal „Dunkel“ oder „Wie Gerät“ ein, und das Gerät merkt es sich.
|
- **Empfehlung: A.** Draußen bei Tag ist hell besser lesbar. Wer abends dunkel will, stellt einmal „Dunkel“ oder „Wie Gerät“ ein, und das Gerät merkt es sich.
|
||||||
|
|
||||||
|
4. **Bewegungs-Sprache (neu 2026-10-16, die Rückfrage aus Beute-Spec §12.4).** Wie viel Bewegung bekommt die App außerhalb der Beute?
|
||||||
|
- A: Wie §17. Die App bleibt ruhig, die Beute ist die eine Bühne. Bewegung zeigt nur, woher etwas kommt, was weg ist und was geklappt hat. Das Abhaken bleibt der eine Moment.
|
||||||
|
- B: Mehr Bewegung in der ganzen App: Karten heben sich, Zahlen zählen hoch, Seitenwechsel mit gleitenden Übergängen (View Transitions). Draußen kostet das Lesezeit und Akku.
|
||||||
|
- C: Keine Bewegung außerhalb der Beute, auch kein Gleiten der Sheets.
|
||||||
|
- **Empfehlung: A.** Hält die Beute als Höhepunkt und die Arbeit am Stand schnell. Die Stufe „Voll“ bleibt Standard; wer am Stand Ruhe will, stellt „Dezent“ ein.
|
||||||
|
|
||||||
|
## 17. Bewegungs-Sprache
|
||||||
|
|
||||||
|
Nachtrag 2026-10-16. Anlass: Die Beute (Spec `2026-10-15-beute-3d-design.md` §9.3, §9.8, §12.4) ist auf Wunsch des Inhabers stark animiert: Glas-Holo, Flug in den Fokus, Heben der Rähmchen, Federn. Die übrige App soll sich wie dieselbe App anfühlen. Dieser Abschnitt ersetzt die Regel „Keine neue Animation“ aus §1. Bestätigung durch den Inhaber: Frage 4 in §16.
|
||||||
|
|
||||||
|
### 17.1 Zweck
|
||||||
|
|
||||||
|
Draußen am Stand hat der Imker Sonne auf dem Display, Handschuhe an und eine Hand frei. Bewegung hat dort genau drei Aufgaben:
|
||||||
|
|
||||||
|
1. **Herkunft zeigen:** Ein Sheet kommt von seiner Kante, ein geöffnetes Teil fliegt aus seinem Stapel. Man sieht, wo man ist.
|
||||||
|
2. **Veränderung zeigen:** Eine Zeile geht weg, die anderen rücken nach. Man sieht, was sich geändert hat.
|
||||||
|
3. **Erfolg bestätigen:** Der Haken zeichnet sich, der Knopf gibt beim Drücken nach. Man weiß, dass der Tipp angekommen ist, auch mit Handschuh.
|
||||||
|
|
||||||
|
Bewegung schmückt nie und hält nie auf. Kein Ziel wartet auf eine Animation, und keine Zahl bewegt sich, während jemand sie liest.
|
||||||
|
|
||||||
|
### 17.2 Stufen
|
||||||
|
|
||||||
|
Es gilt die bestehende Bewegungsstufe je Gerät (`apicore.motion`, §5.1). Die Wörter in der Oberfläche heißen überall „Voll“, „Dezent“ und „Aus“ (die Beute-Spec nennt „Dezent“ an einer Stelle „ruhig“).
|
||||||
|
|
||||||
|
| Stufe | Wann | Ortswechsel (Gleiten, Fliegen, Heben, Nachrücken) | Zustandswechsel (Deckkraft, Farbe) | Ruhebewegung (Beute schwebt, Pegel wogt, Puls) |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `full` „Voll“ | Standard, solange das System nicht reduziert | an, mit den Tokens aus 17.3 | an | an, nur sichtbar und im Vordergrund-Tab |
|
||||||
|
| `subtle` „Dezent“ | gewählt, **oder** „Voll“ plus `prefers-reduced-motion: reduce` | aus, Dinge stehen sofort am Ziel | Überblendung `--motion-fade` (150 ms) | aus |
|
||||||
|
| `off` „Aus“ | gewählt | aus | sofort (globale Regel in `globals.css`) | aus |
|
||||||
|
|
||||||
|
- **Eine Quelle für JavaScript:** `useEffectiveMotionLevel()` aus `src/lib/motion/motion-preference.ts`. Den Hook nutzt die Beute schon (`hive-canvas.tsx`, `hive-visual-slot.tsx`). Er verbindet die gewählte Stufe mit `prefers-reduced-motion` und reagiert sofort auf beides. Ab §5 setzt das Inline-Skript `data-motion` vor dem ersten Bild. `useAppearance().setMotion` löst dabei weiter `MOTION_EVENT` aus, damit Szene und DOM sofort mitziehen.
|
||||||
|
- **Eine Quelle für CSS:** die Variante `motion-full:` (gibt es schon in `globals.css`). Jede Verschiebung, Skalierung oder Drehung steht hinter `motion-full:`. `motion-safe:` fragt nur das System und übergeht die Stufe in der App; für Ortswechsel ist es darum verboten. Farb- und Deckkraft-Übergänge brauchen keine Variante, weil „Aus“ ihre Dauer global auf null setzt.
|
||||||
|
- **`motion` (`motion/react`):** Ein `AppMotion`-Rahmen im `(app)`-Layout setzt `MotionConfig` je Stufe. Bei `full` steht `reducedMotion` auf `"never"`, weil der Hook das System schon einrechnet. Bei `subtle` steht es auf `"always"`: Dann laufen nur Deckkraft und Farbe. Bei `off` gilt `transition={{ duration: 0 }}`.
|
||||||
|
- **Sonne ändert keine Bewegung.** Sonne betrifft nur Kontrast und Größen. Das Umschalten von Farbschema, Kontrast und Stufe wirkt sofort und ohne Übergang (§5.3).
|
||||||
|
|
||||||
|
### 17.3 Tokens
|
||||||
|
|
||||||
|
Die Werte kommen aus dem, was die Beute schon ausliefert, und aus den bestehenden CSS-Bewegungen. Darum fühlen sich Bühne und App gleich an. CSS-Variablen stehen in `globals.css` (`:root`). Den Spiegel in TypeScript liefert `src/lib/motion/tokens.ts`. Die Beute liest ihre Feder und ihre Überblendung künftig aus dieser Datei (`quality.ts`, `use-hive-motion.ts`), damit es nur eine Quelle gibt.
|
||||||
|
|
||||||
|
| Token | Wert | Herkunft | Verwendung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `--motion-fade` | 150 ms | `SUBTLE_FADE_SECONDS` (Beute), Overlay der Sheets | jede Überblendung, jeder Austritt, alle Wechsel bei „Dezent“ |
|
||||||
|
| `--motion-enter` | 200 ms | `ui/sheet.tsx` (200 ms) | Eintritt von Sheet, Dialog, Popover, Menü, Seite und neuer Listenzeile |
|
||||||
|
| `--motion-settle` | 350 ms | `DAMPING = 12` (Beute: nach 350 ms in Ruhe) | Ortswechsel ohne Feder: Zurückfedern, Aufklappen eines Abschnitts |
|
||||||
|
| `--motion-moment` | 420 ms | `.honey-check` (Wabe füllt sich) | der eine Moment: Abhaken |
|
||||||
|
| `--ease-out-expo` | `cubic-bezier(0.16, 1, 0.3, 1)` | Bestand in `globals.css` | alle Eintritte, der Moment |
|
||||||
|
| `--ease-in` **neu** | `cubic-bezier(0.4, 0, 1, 1)` | – | alle Austritte (schneller weg als da) |
|
||||||
|
| `--ease-spring` **neu** | `linear(…)`, aus der Feder unten erzeugt | `SPRING_STIFFNESS 210`, `SPRING_DAMPING 21` (`quality.ts`, Dämpfungsgrad ≈ 0,72, in Ruhe nach ≈ 0,5 s) | CSS-Ortswechsel mit leichtem Überschwingen, Dauer 500 ms |
|
||||||
|
| `SPRING_FLIGHT` (TS) | `{ type: "spring", stiffness: 210, damping: 21, mass: 1 }` | dieselbe Feder | `motion`: Nachrücken (`layout`), Loslassen nach dem Ziehen |
|
||||||
|
| `--motion-shift` **neu** | 8 px | Beute hebt ein Teil 8 mm, `next-step-in` 8 px | Weg beim Eintritt von Inhalt (Seite 6 px bleibt) |
|
||||||
|
| Sheet-Weg | 2,5 rem | Bestand `ui/sheet.tsx` | Sheet gleitet von seiner Kante |
|
||||||
|
|
||||||
|
**Grenzen:**
|
||||||
|
- Kein DOM-Übergang dauert länger als `--motion-moment` (420 ms).
|
||||||
|
- Ausnahmen gibt es nur zwei:
|
||||||
|
- die Beute-Bühne (Federflug ≈ 0,5 s, Ruhebewegung)
|
||||||
|
- das Wegfalten der Heute-Zeile nach dem Abhaken (`EXIT_DELAY_MS.full = 720`). Gespeichert ist dort sofort, „Rückgängig“ steht im Toast.
|
||||||
|
- Austritte sind nie länger als `--motion-fade`.
|
||||||
|
|
||||||
|
### 17.4 Regeln je Muster
|
||||||
|
|
||||||
|
| Muster | Voll | Dezent | Werkzeug |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Seite** (Routenwechsel) | Eintritt: Deckkraft 0 → 1 und 6 px nach oben, `--motion-enter`, `--ease-out-expo`. Kein Austritt: Die alte Seite verschwindet sofort, die Navigation wartet nicht. | nur Deckkraft, `--motion-fade` | CSS `animate-page-in` (`page-transition.tsx`, Dauer heute 260 ms → Token) |
|
||||||
|
| **Sheet, Dialog, Popover, Menü** (öffnen, schließen) | Sheet gleitet 2,5 rem von seiner Kante, Dialog und Popover blenden mit `scale(0.97 → 1)` ein; Eintritt `--motion-enter` mit `--ease-out-expo`, Austritt `--motion-fade` mit `--ease-in` auf demselben Weg zurück; Hintergrund nur Deckkraft. Der Fokus springt beim Öffnen sofort hinein, nicht erst nach der Animation. Beim Auf- oder Zugehen der Bildschirmtastatur bewegt sich das Sheet nicht. | nur Deckkraft | CSS über `data-starting-style`/`data-ending-style` von Base UI (Muster steht in `ui/sheet.tsx`). Dialog heute 100 ms, Sheet heute `ease-in-out` → Tokens. |
|
||||||
|
| **Listenzeile hinzu** (durch den Nutzer: Eintrag gespeichert, Zeile ergänzt) | nur die neue Zeile: Deckkraft und `--motion-shift`, `--motion-enter`. Kein Stagger, kein Einblenden beim Laden einer Liste. Daten stehen sofort da. | Deckkraft, `--motion-fade` | CSS (`animate-in` aus `tw-animate-css` oder `@starting-style`) |
|
||||||
|
| **Listenzeile weg** | Zeile blendet aus (`--motion-fade`), die übrigen rücken mit `SPRING_FLIGHT` nach. Das läuft über `transform` (`layout`), nicht über die Höhe. | Zeile blendet aus, der Rest steht sofort am Ziel | `motion`: `AnimatePresence` und `m.li layout`. Gilt für Heute-Liste, Aufgaben, Warteschlangen-Sheet und Sprachnotizen. |
|
||||||
|
| **Umsortieren** | `layout` mit `SPRING_FLIGHT` | sofort | `motion` |
|
||||||
|
| **Abschnitt auf/zu** (Aufklapp-Karten, „Als Tabelle“) | Pfeil dreht sich (`--motion-settle`). Der Inhalt erscheint sofort, ohne Höhen-Animation. | Pfeil springt | CSS `motion-full:transition-transform` (Bestand, bisher `motion-safe:`) |
|
||||||
|
| **Erfolg, Bestätigung** | **Abhaken** ist der eine Moment: Wabe füllt sich (`--motion-moment`), Haken zeichnet sich, Zeile faltet weg. **Speichern** endet mit Toast und Navigation; kein Konfetti, kein Knopf, der seine Form ändert. **Knopf-Druck:** Fläche wechselt sofort, bei Voll zusätzlich `scale(0.97)` beim Drücken, 100 ms. | Wabe und Haken blenden in 120 ms (Bestand), Knopf ohne Skalierung | CSS (`.honey-check`, `ui/button.tsx` `motion-full:active:scale-[0.97]`) |
|
||||||
|
| **Fokuswechsel** | Der Fokusring erscheint sofort und wird nie animiert. Programmatischer Fokus (erstes Fehlerfeld, geöffnetes Sheet, Live-Region) springt. Scrollen dorthin ist `smooth` nur bei Voll und bei weniger als einer Bildschirmhöhe, sonst sofort. Auf der Beute-Bühne fliegt die Kamera mit (Beute §9.4). | Scrollen sofort | `scrollBehaviorFor(level, distance)` in `src/lib/motion/tokens.ts` |
|
||||||
|
| **Laden, Skelett** | Das Skelett erscheint erst nach 150 ms, damit kurze Ladezeiten nicht blitzen. Pulsieren nur bei Voll (Deckkraft, 2 s). Spinner nur in Knöpfen ab 300 ms Wartezeit und immer mit Wort („Speichert …“). | Skelett ruhig; der Spinner wird ein ruhendes Icon mit Wort | CSS `motion-full:animate-pulse`, `motion-full:animate-spin` (bisher `motion-safe:`) |
|
||||||
|
| **Läuft gerade** (Aufnahme, Synchronisieren) | Punkt pulsiert (Deckkraft) | ruhiger Punkt; das Wort („Aufnahme läuft“) trägt den Zustand | CSS |
|
||||||
|
| **Hover** (nur `pointer-fine`) | nur Farbe und Rahmen, keine Verschiebung im DOM. Das Heben der Karten (`ui/card.tsx`) und das Vergrößern der Icons in der Seitenleiste entfallen. Auf der Beute-Bühne hebt sich das Teil (Beute §9.4), weil es dort zeigt, was der nächste Klick öffnet. | Farbe | CSS |
|
||||||
|
| **Wabenmotiv** | Die Wabenzelle bewegt sich nur an zwei Stellen: beim Abhaken (`.honey-check`) und auf der Beute-Bühne. Waben-Standkarte, Mini-Waben, Rad, Lebenslinie, `BrandMark` und `AreaIcon` stehen still (6.8, 8.5, 9.5 bleiben). Ein Ladezeichen in Wabenform gibt es nicht. | – | – |
|
||||||
|
| **Toast** | Sonner-Standard (gleitet), Dauer und Kurve auf die Tokens | ohne Gleiten, nur Deckkraft | CSS-Überschreibung an `[data-sonner-toaster]`, falls Sonner das nicht selbst tut |
|
||||||
|
|
||||||
|
### 17.5 Was nie animiert
|
||||||
|
|
||||||
|
- **Datenwerte, die der Imker liest:**
|
||||||
|
- Zahlen, Ampeln, Zählungen, Balken, Kurven, Zellfüllungen, der Rad-Zeiger und die Zahl im Pegel-Etikett.
|
||||||
|
- Nichts zählt hoch, kein Balken wächst, keine Linie zeichnet sich.
|
||||||
|
- Ein neuer Wert nach dem Sync steht sofort da.
|
||||||
|
- Bestand, der das verletzt: `stat-bar-list.tsx` (`transition-[width] duration-700`) verliert den Übergang.
|
||||||
|
- Auf der Beute-Bühne wogt die Oberfläche des Pegels; seine Zahl springt.
|
||||||
|
- **Fehler und Warnungen:**
|
||||||
|
- Feldfehler, `role="alert"`, Warn- und Sperr-Hinweise (Sperrbezirk, Wartezeit, 409) erscheinen sofort.
|
||||||
|
- Kein Schütteln, kein Einblenden, kein verzögertes Erscheinen.
|
||||||
|
- **Während der Eingabe am Stand:**
|
||||||
|
- Das Feld selbst, die Zahl im Stepper (keine rollenden Ziffern), die Umgebung des fokussierten Felds.
|
||||||
|
- Nichts verschiebt sich, kein Toast legt sich über Feld oder Speichern-Leiste, nichts leitet von selbst weiter.
|
||||||
|
- Eine Diktat- oder Sprachnotiz-Aufnahme bewegt nur ihren Status-Punkt.
|
||||||
|
- **Fokusring, Sonne an/aus, Farbschema, Druck und PDF.**
|
||||||
|
- **Warten:**
|
||||||
|
- Kein Ziel ist während eines Eintritts gesperrt (`pointer-events` bleiben an).
|
||||||
|
- Kein Klick wartet auf einen Austritt.
|
||||||
|
- Einzige gewollte Verzögerung ist das Wegfalten der Heute-Zeile (17.3).
|
||||||
|
|
||||||
|
### 17.6 Leistung
|
||||||
|
|
||||||
|
- **Nur `transform` und `opacity`** bewegen sich.
|
||||||
|
- Für Zustandswechsel kommen Farbe (`color`, `background-color`, `border-color`, `fill`, `stroke`) und `clip-path` dazu (`.honey-check`).
|
||||||
|
- Nie animiert werden `width`, `height`, `top`, `left`, `margin`, `padding`, `grid-template-rows`, `box-shadow`, `filter` und `backdrop-filter`.
|
||||||
|
- Bestand, der das verletzt:
|
||||||
|
- `.today-row` faltet über `grid-template-rows` → `motion` `layout` (17.4)
|
||||||
|
- `next-step-in` animiert `filter: blur(6px)` → nur Deckkraft und `--motion-shift`
|
||||||
|
- `stat-bar-list` animiert die Breite → ohne Übergang
|
||||||
|
- **Kein `transition-all`:** Die Übergänge nennen ihre Eigenschaften (`transition-colors`, `transition-[opacity,transform]`). Heute gibt es 8 Stellen, 7 davon in `ui/*`.
|
||||||
|
- **Kein Layout-Thrash:**
|
||||||
|
- Keine Messung (`getBoundingClientRect`, `offsetHeight`) in einem Animationsschritt.
|
||||||
|
- `motion` misst bei `layout` einmal zu Beginn und bewegt dann per `transform`.
|
||||||
|
- Kein React-State pro Bild. DOM-Bewegung läuft über CSS oder über das WAAPI von `motion`.
|
||||||
|
- **`will-change`** setzt nur `motion` selbst, nie dauerhaft in CSS. `backdrop-blur` nur auf ruhenden Flächen, in Sonne gar nicht (§4.3).
|
||||||
|
- **Ziel:** 60 fps auf einem Mittelklasse-Handy. Gemessen wird mit einem Performance-Trace bei 4-facher CPU-Drosselung und 390 px. Bei Sheet auf/zu, Abhaken mit Wegfalten, Zeile entfernen und Seitenwechsel ist kein Frame länger als 32 ms, und während der Bewegung gibt es keinen Layout-Eintrag außer der einen `layout`-Messung.
|
||||||
|
- **Bundle:**
|
||||||
|
- `motion` nur über `LazyMotion` mit `strict`. Elemente kommen als `m.*` aus `motion/react-m`, die Features (`domMax` wegen `layout`) asynchron.
|
||||||
|
- Geladen wird nur im `(app)`-Layout, nicht auf Login und Herkunft.
|
||||||
|
- Budget: höchstens 30 kB gzip, im Build gemessen wie das 3D-Budget.
|
||||||
|
- Die three.js-Szene nutzt `motion` nicht (eigene Federn in `use-animated-node.ts`).
|
||||||
|
|
||||||
|
### 17.7 Abnahme
|
||||||
|
|
||||||
|
1. **Wächter** (`src/styles/baukasten-guard.spec.ts`, Regelgruppe „Bewegung“, Ausnahmen mit Grund):
|
||||||
|
- kein `motion-safe:` vor `translate`, `scale`, `rotate`, `slide-`, `zoom-`, `animate-` (→ `motion-full:`)
|
||||||
|
- kein `transition-all`
|
||||||
|
- kein `transition-[width|height]` und kein `duration-` über 420 ms (Ausnahme `components/hive/**`)
|
||||||
|
- `hover:` mit `translate` oder `scale` nur in `components/hive/**`
|
||||||
|
- `motion/react` liefert nur `LazyMotion`, `MotionConfig`, `AnimatePresence` und Hooks; Elemente nur als `m.*`
|
||||||
|
- Keyframes und Übergänge in `globals.css` animieren keine Eigenschaft aus der Verbotsliste in 17.6 (der Test liest die Datei)
|
||||||
|
2. **Tokens** (`src/lib/motion/tokens.spec.ts`):
|
||||||
|
- Die CSS-Variablen entsprechen den TS-Konstanten.
|
||||||
|
- `SPRING_FLIGHT` hat dieselben Werte wie die Feder der Beute (eine Quelle).
|
||||||
|
- Die Stützpunkte von `--ease-spring` weichen höchstens 0,02 von `springStep` ab.
|
||||||
|
3. **Komponenten:**
|
||||||
|
- `AppMotion` setzt `reducedMotion` je Stufe.
|
||||||
|
- Bei „Aus“ ist eine entfernte Zeile sofort weg, bei „Dezent“ ohne Verschiebung.
|
||||||
|
- `scrollBehaviorFor` liefert `smooth` nur bei Voll und kurzer Strecke.
|
||||||
|
4. **Browser** (Chrome DevTools):
|
||||||
|
- Jedes Muster aus 17.4 einmal in Voll, Dezent und Aus, dazu `prefers-reduced-motion: reduce` per `emulate`.
|
||||||
|
- Bei Dezent und bei reduce verschiebt sich nichts, bei Aus geht alles sofort.
|
||||||
|
- Dazu der Performance-Trace aus 17.6.
|
||||||
|
5. **Draußen-Test** (13.4) bekommt Aufgabe 6: eine Aufgabe mit Handschuh abhaken. Bestanden, wenn der Haken gesehen wird und niemand auf die Liste wartet.
|
||||||
|
6. **Review:** einmal `review-animations` bzw. `fixing-motion-performance` über den Diff (Skill-Routing: erst Konzept, dann CSS, dann `motion`, GSAP nicht).
|
||||||
|
|
||||||
|
### 17.8 Beute-Bühne im T5-Rahmen
|
||||||
|
|
||||||
|
- Die Bühne ist eine eigene, immer dunkle Fläche mit eigenen Tokens (`--holo-*`).
|
||||||
|
- Nur hier gibt es Glas, Leuchten, Milchglas-Elemente und Ruhebewegung.
|
||||||
|
- §3.6 und die Verbotsliste gelten außerhalb der Bühne. Wächter-Ausnahmen gelten nur für `components/hive/**` und `app/(app)/colonies/[id]/hive/**`.
|
||||||
|
- Bewegung auf der Bühne folgt Beute §9.8. Die Werte stammen aus denselben Tokens (17.3).
|
||||||
|
- Fokus auf der Bühne: Der Ring aus §4.6 in `--primary-ink` hat auf `--holo-bg` weniger als 3:1. Deshalb setzt ein Rahmen `.holo-stage` `--ring: var(--holo-edge-strong)`. `contrast.spec.ts` prüft das Paar (≥ 3) und die Etiketten `--holo-label-fg` auf `--holo-label-bg` (≥ 7).
|
||||||
|
- **Sonne:**
|
||||||
|
- `--holo-*` bekommt Sonne-Sätze (Bühne schwarz, Kanten reinweiß).
|
||||||
|
- Die Szene liest die Tokens beim Wechsel der Darstellung neu ein; heute geschieht das einmalig in `hive-scene/theme.ts`.
|
||||||
|
- Ohne Bloom, Kanten 2 px, Rähmchen-Arten als Schraffur, Etiketten 16 px (Beute §9.3).
|
||||||
Reference in new issue
Block a user