fix(layout): kollidierenden und überlaufenden Text beseitigen

Gemeldeter Fehler: In der Ringlegende klebten Bezeichnung und Wert aneinander
("Gestellt189,1 T€"). Ursache war eine eigene Regression — beim Beheben der
abgeschnittenen Legendennamen war das truncate entfallen, ohne Ersatz. Damit
schrumpft die Namensspalte durch min-w-0 + grow auf null Breite, und der Text
läuft sichtbar über seinen Kasten hinaus:

  1280 px  Kastenbreite 0 px, Text 43 px darüber hinaus
  1440 px  Kastenbreite 25 px, Text 18 px darüber hinaus
  1600 px  unauffällig

Genau deshalb war es auf den Screenshots nicht zu sehen: Der Fehler tritt nur in
bestimmten Fensterbreiten auf.

Neu: scripts/verify-layout.mjs + npm run verify:layout. Lädt alle sechs Seiten in
sieben Breiten (1024–1920) und misst jede Textzeile über Range.getClientRects.
Gemessen wird auf Textzeilen, nicht auf Elementkästen — sonst melden benachbarte
Tabellenzellen mit Innenabstand falsch positiv. Mindestabstand 8 px = Carbon
$spacing-03. Der Lauf fand 110 Fehler; alle behoben:

- Ringlegende: Container-Query statt fester Zweispaltigkeit. Legende sitzt rechts
  vom Ring, wenn Platz ist, sonst darunter — Carbon: "positioned to the right of
  the chart when space is plentiful". Namen brechen um statt überzulaufen.
- Auslastungs-Achse: Beschriftungsdichte staffelt sich über drei Container-Stufen,
  vorher überlappten die Zahlen in schmalen Karten um bis zu 16 px.
- Handlungsbedarf und Kennzahl-Labels: break-words statt Überlauf.
- Kartentitel und -untertitel: break-words, weil Dateipfade keine Leerzeichen
  zum Umbrechen haben.

Dokumentiert als DESIGN-SYSTEM.md §9 mit Begründung, warum das ein gemessener
Prüfer sein muss und keine Sichtprüfung.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
This commit is contained in:
dejanandClaude Opus 5 committed 2026-08-29 19:32:55 +02:00
1 parent 0875ae2688
commit 351a26efa4
10 files changed
+232 -23

No files matched your search

+34 -1
View File
@@ -269,7 +269,40 @@ Ringsegmente werden hervorgehoben (die übrigen auf 35–45 % Deckkraft).
Tabellenzeilen bekommen **keinen** Hover, solange sie nicht anklickbar sind —
ein Hover ohne Ziel verspricht eine Interaktion, die es nicht gibt.
## 9. Fokus und Kontrast
## 9. Textabstände und Umbruch
Zwei Regeln, die Carbon nicht als Zahl nennt, ohne die eine Oberfläche aber
kaputtgeht:
**Mindestens 8 px zwischen benachbarten Textelementen** (Carbon-Spacing-Token
`$spacing-03`, der kleinste Abstand der Skala). Alles darunter liest sich als
zusammengehörig, bei 0 px kleben Beschriftung und Wert aneinander.
**Kein Text läuft aus seinem Kasten.** Die häufigste Ursache ist
`min-w-0` zusammen mit `grow` ohne `truncate` oder `break-words`: Der Kasten
schrumpft auf null, der Text bleibt stehen und läuft in das Nachbarelement.
Deshalb trägt jedes Textfeld, das schrumpfen kann, entweder `truncate` (wenn
Kürzen vertretbar ist) oder `break-words` (wenn nicht). Dateipfade und lange
Einzelwörter brauchen zwingend `break-words` — sie haben keine Leerzeichen zum
Umbrechen.
**Legenden weichen nach unten aus.** Carbon sagt, die Legende gehört rechts
neben das Diagramm, „when space is plentiful". Ist der Platz nicht reichlich,
rutscht sie unter das Diagramm, statt Namen und Werte ineinanderlaufen zu
lassen. Gesteuert über **Container-Queries** (`@container` / `@md:`), nicht über
den Viewport: Dieselbe Karte nimmt mal ein Drittel und mal die halbe
Seitenbreite ein — der Viewport weiß davon nichts.
**Nachprüfbar gemacht:** `npm run verify:layout` lädt alle sechs Seiten in
sieben Fensterbreiten (1024–1920) und misst jede Textzeile. Kollisionen unter
8 px und Text, der seinen Kasten verlässt, brechen den Lauf ab.
Warum das ein eigener Prüfer sein muss: Diese Fehler treten **nur in bestimmten
Breiten** auf. Die Ringlegende auf der Finanzseite kollidierte bei 1280 und
1440 px und war bei 1600 px unauffällig — auf einem Screenshot also unsichtbar.
Auge und Einzelbild reichen dafür nicht.
## 10. Fokus und Kontrast
**Fokus** ist eine einzige Regel in `app/globals.css` statt `focus:`-Klassen an
jedem Element: