Files
tickte-system/docs/superpowers/plans/2026-10-05-weisses-design-p1-fundament.md
T

35 KiB
Raw Blame History

WOMS „Weißes Design“ P1: Fundament – Umsetzungsplan

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.

Goal: Nach P1 ist das ganze Ticketsystem weiß und lesbar:

  • Das Menü ist eine Textliste.
  • Das Login ist neu.
  • Alle Dialoge sind weiß.
  • Die neuen Bausteine stehen für P2 bis P9 bereit.

Die Innenlayouts der Seiten bleiben vorerst alt.

Architecture: src/styles/global.css bekommt fünf Blöcke:

  1. Tokens und Aliase
  2. Basis (Reset, Hilfsklassen, Links)
  3. Bausteine (Prototyp-Klassen)
  4. KOMPAT (Altklassen im neuen Look, bis P10)
  5. Bereichs-CSS (Finanzen, Rechtliches), bis auf Pflicht-Fixes unverändert

Gleichnamige Altdefinitionen werden vor dem Einfügen der Bausteine gelöscht. Die Komponenten unter src/components/ui/ behalten ihre Schnittstellen. Neu kommen Dialog, Stat, Logo und statusTone hinzu.

Tech Stack: React 18, react-router-dom 6, Vite 5, reines CSS. Tests mit node --test, Screenshots mit dem Werkzeug aus P0.

Spec: docs/superpowers/specs/2026-10-04-weisses-design-design.md, Abschnitte „Gestaltungsregeln“, „Design-Tokens“, „Bausteine“, „Übergangsschicht KOMPAT“ und Paket P1. Die Prototyp-Werte stehen in /root/reports/ticketsystem-design-2026-10-04/quelle/styles.css. Globale Regeln, Befehlsregeln (env.sh, check.sh) und gemeinsame Schnittstellen stehen in 2026-10-05-weisses-design-00-uebersicht.md.

Global Constraints

Es gelten die Regeln der Übersicht. Für P1 kommt hinzu:

  • Jeder Run-Block beginnt mit source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh. Danach gelten $WT, $W und $TMPDIR.
  • Werte für Tokens, Abstände, Größen und Schatten werden aus der Spec bzw. aus quelle/styles.css übernommen und nicht neu erfunden.
  • Nicht übernommen werden html[data-accent], .proto* und .nav-gap.
  • Verhalten, Routen und Auth bleiben unverändert. Ausnahmen:
    • Der Footer entfällt.
    • Das Einklappen entfällt.
    • Die Schublade unter 860 px kommt neu dazu.
  • Seiten in src/pages/* werden nur dort angefasst, wo dieser Plan es nennt:
    • LoginPage
    • ProjectsPage: Wurzelklasse
    • AdminPage: Wurzelelement
    • ServerpulsPage: Diagrammfarben
  • Die Spec erlaubt keine neue robots-Meta-Zeile. index.html bekommt nur das Favicon.

Review Focus

  • Übersichtspunkt 2 (lange Inhalte am Handy): overflow-x ist im Gate p1 für alle Ansichten scharf.
  • Übersichtspunkt 4 (Tastatur): Die Esc- und Sperrlogik des Dialogs prüft ein Lib-Test in Task 7.
  • Altseiten: Jede Altseite bleibt mit KOMPAT lesbar. Das prüft das Gate p1 mit dark-surface und low-contrast, auch für Verläufe.
  • Alt-Dialoge: Die zehn Alt-Dialoge müssen bei 390×700 und 1440×700 bis zum Speichern-Knopf erreichbar sein. Das prüft submitReachable in jeder dlg-*-Ansicht.

Task 0: Paketstart

  • Step 1: Stand prüfen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git merge-base --is-ancestor design-p0 HEAD && git log --oneline origin/test..feature/terminplaner-tp2 -- src/styles src/components/ui src/App.jsx src/components/Navbar.jsx src/pages/LoginPage.jsx src/lib/navLinks.js src/lib/ticketStatus.js && git rebase origin/test && "$W/check.sh" Expected:

  • Exit 0.
  • Die TP2-Zeile gibt nichts aus. Gibt sie etwas aus: stoppen und den Eigentümer fragen. Empfehlung: TP2 zuerst nach test bringen.

Task 1: statusTone

Files:

  • Modify: src/lib/ticketStatus.js (neuer Export nach statusGroup)
  • Test: tests/unit/ticketStatus.test.mjs (neu)

Interfaces:

  • Consumes: statusGroup(status) aus derselben Datei. Es liefert die Gruppen-ID oder null.

  • Produces: statusTone(status) → '' | 'blue' | 'orange' | 'violet' | 'green' | 'plain'.

  • Step 1: Failing Test

const { statusTone } = await loadModule('src/lib/ticketStatus.js')
test('statusTone: Ton je gespeichertem Wert', () => {
  for (const s of ['Open', '', undefined, null, 'Completed']) assert.equal(statusTone(s), '')
  for (const s of ['Occupied', 'Assigned', 'Added Info', 'In Kontakt']) assert.equal(statusTone(s), 'blue')
  for (const s of ['Awaiting', 'Halted']) assert.equal(statusTone(s), 'orange')
  assert.equal(statusTone('In Test'), 'violet')
  for (const s of ['Closed', 'Zugesagt']) assert.equal(statusTone(s), 'green')
  for (const s of ['Cancelled', 'Aborted', 'Abgesagt']) assert.equal(statusTone(s), 'plain')
})

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/ticketStatus.test.mjs Expected: FAIL, statusTone is not a function.

  • Step 2: Implementieren

Zuordnung über statusGroup:

Gruppe Ton
open ''
working 'blue'
waiting 'orange'
review 'violet'
done 'green'
cancelled 'plain'
null ''

statusClass bleibt bis P2.

  • Step 3: Grün

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/ticketStatus.test.mjs && "$W/check.sh" --nur-tests Expected: PASS, Exit 0.

  • Step 4: Commit
git add src/lib/ticketStatus.js tests/unit/ticketStatus.test.mjs
git commit -m "Ticketstatus: statusTone fuer die neuen Badge-Toene" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 2: Tokens, Aliase, Basis, Schrift

Files:

  • Modify: src/styles/global.css
    • Zeilen 1-10 (@font-face) löschen
    • :root, alte Zeilen 18-71, ersetzen
    • body/Scrollbar/::selection, alte Zeilen 73-106, ersetzen
    • alte Hilfsregeln .muted/.faint (um Zeile 1051) ersetzen
  • Modify: src/pages/AdminPage.css (Zeilen 2-3, --admin-line*, löschen)
  • Delete: src/assets/fonts/lato-regular.ttf, src/assets/fonts/OFL-Lato.txt
  • Modify (Werkzeug): $W/views.mjs bekommt die Ansicht basis-weiss

Interfaces:

  • Produces: alle Tokens und Aliase der Spec, dazu die Basis- und Hilfsklassen aus der Übersicht.

  • Step 1: Prüf-Ansicht (failing)

basis-weiss öffnet /tickets. Der Schritt prüft, dass getComputedStyle(document.body).backgroundColor === 'rgb(255, 255, 255)' und dass font-family mit -apple-system beginnt.

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t2" --only basis-weiss --gate p0 Expected: Exit 1 mit step-failed.

  • Step 2: :root ersetzen

  • Den Token-Block der Spec wörtlich übernehmen.

  • Darunter folgt die Alias-Tabelle der Spec. Sie enthält --admin-line und --admin-line-strong.

  • Die beiden Zeilen in AdminPage.css entfallen.

  • Step 3: Basis setzen

Aus quelle/styles.css Zeilen 63-91 übernehmen. Zusätzlich gilt:

Regel Wert
html color-scheme: light
body font-family: var(--font); font-size: 14px; line-height: 20px; color: var(--text); background: var(--bg); -webkit-font-smoothing: antialiased, ohne overflow: hidden
Formularelemente button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer } und input, select, textarea { font: inherit; color: inherit }
a color: inherit; text-decoration: none
a:not([class]) Unterstrich in --line-strong, text-underline-offset: 3px, beim Überfahren text-decoration-color: currentColor
Hilfsklassen .num (tabular-nums), .muted (--text-2), .faint (--text-3), .small 13px, .xs 12/16, .strong 600, .nowrap
.link, .link:hover, .link-danger, .link-danger:hover Werte aus dem Prototyp
:focus-visible outline: 2px solid #111111; outline-offset: 2px
:disabled opacity: .55; cursor: not-allowed
[readonly] background: var(--bg-subtle)
  • Step 4: Schrift entfernen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git rm -q src/assets/fonts/lato-regular.ttf src/assets/fonts/OFL-Lato.txt && rg -n "lato|Lato" src Expected: keine Treffer.

  • Step 5: Grün

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t2" --only basis-weiss --gate p0 Expected: Exit 0.

  • Step 6: Commit
git add src/styles/global.css src/pages/AdminPage.css
git commit -m "Design: weisse Tokens, Aliase, Basis und Hilfsklassen, Systemschrift" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 3: Bausteine-CSS

Files:

  • Modify: src/styles/global.css: Altdefinitionen gleichnamiger Klassen löschen, Block „Bausteine“ hinter der Basis anlegen

Interfaces:

  • Produces: alle Klassen aus der Übersicht („CSS-Klassen in global.css“).

  • Step 1: Gleichnamige Altdefinitionen löschen

Gelöscht werden die Regeln dieser Selektoren samt ihrer Varianten (Hover, Kinder, Media-Queries). Die Zeilenangaben beziehen sich auf den Stand 603d08c, gesucht wird nach dem Selektor:

Selektor Zeilen (603d08c)
.btn und .btn:hover, ohne .btn-dark/.btn-teal/.btn-green (die gehen nach KOMPAT bzw. sterben in Task 8) ab 188
.table, .table th, .table td, .table td[rowspan] 236-275
.overlay und .overlay-close (Neufassung in KOMPAT, Task 4) ab 563
.page 867
.badge (Basis) 909-915
.urgent-toggle 971-976
.list-row 997-1005
.toolbar 1018
.seg 1019-1024
.empty 1028
.field 1032

Der übrige alte Block „REDESIGN“ (796-1049) bleibt bis Task 4. Dazu gehören .ui-card*, .ui-btn*, .badge-*, .kpi*, .tcard*, .ui-table, .list, .ticket-tabs/.ticket-tab* und die Hilfsregeln .mt-*, .flex*, .gap-*, .grow, .wrap und .text-ellipsis.

  • Step 2: Bausteine übernehmen

Aus quelle/styles.css werden diese Abschnitte übernommen:

  • „Grundgerüst“ ohne .nav-gap
  • „Seitenkopf“
  • „Knöpfe“
  • „Status-Marken“
  • „Reiter“
  • „Filterzeile“
  • „Tabellen“
  • „Kennzahlen / Schlüsselwerte“
  • „Ticket-Kopf“
  • „Listen“
  • „Dialog“
  • „Anmeldung“

Dazu kommen die Media-Queries 1100 und 860. Ausgeschlossen sind dort .proto*, .ws*, .week, .day* und .appt (sie kommen mit P2 und P3) sowie html[data-accent].

Zusätzlich:

Regel Wert
.page section + section margin-top: 36px
.nav-dot background: var(--nav-dot)
.nav-dot.critical background: var(--danger)
.dialog-wide max-width: 760px
.msg 13px
.msg.err Farbe --red-fg
.msg.ok Farbe --green-fg
.msg.warn Farbe --orange-fg
.loading padding: 56px 0; text-align: center; color: var(--text-2)
.v.neg Farbe --red-fg
.stat .sub font-size: 12px; line-height: 16px; color: var(--text-2)
.badge.plain Farbe --text-2
.tab.active, .tab[aria-selected="true"] aktiver Reiter
.seg button.on, .seg button[aria-pressed="true"] aktiver Teil
.autocomplete-list background: var(--bg); box-shadow: var(--shadow-pop); border-radius: 8px
.autocomplete-item.active, .autocomplete-item:hover background: var(--bg-hover)
.datetime-pair zwei Felder nebeneinander, Abstand 8px, kein color-scheme: dark
[aria-invalid="true"] und .is-invalid an .input, .select, .textarea, .form-control box-shadow: 0 0 0 1px var(--danger), 0 0 0 4px var(--danger-ring)
  • Step 3: Jede Basisregel genau einmal

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && for s in btn table overlay page badge urgent-toggle list-row toolbar seg empty field; do printf '%s ' "$s"; rg -c "^\.$s *\{" src/styles/global.css; done Expected: Hinter jedem Namen steht 1.

  • Step 4: Bauen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" --nur-build Expected: Exit 0.

  • Step 5: Commit
git add src/styles/global.css
git commit -m "Design: Bausteine aus dem Prototyp, gleichnamige Altregeln ersetzt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 4: KOMPAT und tote Klassen

Files:

  • Create: $W/css-klassen.mjs
  • Create: docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md
  • Modify: src/styles/global.css (Block „KOMPAT“, Legacy löschen)

Interfaces:

  • Produces:

    • Der KOMPAT-Block nach der Spec-Tabelle „Übergangsschicht KOMPAT“. P10 entfernt ihn.
    • css-klassen.mjs <worktree> [--ohne-ausnahmen] gibt JSON aus: { defined, used, dead, undefinedUsed }.
  • Step 1: Skript schreiben

„Benutzt“ ist eine Klasse, wenn eine dieser Bedingungen zutrifft:

  • Sie steht als Wort in einem className-Literal oder Template-String in src/**/*.jsx.
  • Sie steht in einer Zeichenkette in src/lib/*.js oder src/hooks/*.js.
  • Ein Template-Präfix deckt sie ab: xxx-${…} gilt für xxx-*.
  • Sie steht in der Ausnahmeliste: fin-neg, fin-fixed-off, legal-tone-*, legal-version-*, badge-*, status-*, ticket-tab-active, ui-btn-*, side-dot-*, woms-chat, chat-*. Mit --ohne-ausnahmen entfällt diese Liste.

„Definiert“ ist eine Klasse, wenn sie in einem Selektor in global.css, chat.css, serverpuls.css, admin-areas.css oder pages/AdminPage.css steht.

  • Step 2: Liste erzeugen und gegenprüfen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/css-klassen.mjs" "$WT" > "$TMPDIR/klassen.json"

Danach für jeden Kandidaten in dead: rg -w <name> src --glob '!*.css'. Ein Kandidat gilt nur bei 0 Treffern als tot. Die bestätigte Liste kommt mit Klassenname und definierender Datei:Zeile in 2026-10-05-weisses-design-p1-tote-klassen.md.

Expected: etwa 66 Namen. Liegt die Zahl unter 40 oder über 90, wird die Abweichung dort begründet.

  • Step 3: Legacy löschen, KOMPAT anlegen

Gelöscht werden:

  • die Regeln der bestätigten toten Klassen
  • alle verbliebenen !important
  • .side* außer .side-count und .side-logo (die gehen nach KOMPAT)
  • .footer, .navbar*, .ticket-row*, .worksheet-expansion*, .slider, .drop-zone, .modern-card
  • die Legacy-Responsive-Regeln (alt 789-794)
  • die Keyframes slideDown, fadeIn und slideIn
  • .kpi*, weil Kpi ab Task 6 Stat rendert

Danach wird KOMPAT laut Spec-Tabelle angelegt, markiert mit /* ===== KOMPAT (bis P10) ===== */. Der Block enthält auch .page .page und .page .main-content, die Abstand und Breite neutralisieren.

  • Step 4: Keine benutzte Klasse ohne Regel

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/css-klassen.mjs" "$WT" | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.undefinedUsed.join('\n'))" Expected: Nur diese Namen tauchen auf:

  • p-4, btn-sm, status-filter, supporters-editor, timeline

  • die Bootstrap-Namen aus WorksheetList.jsx (heute schon wirkungslos)

  • Step 5: Altseiten ansehen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t4" --gate p1; echo "exit $?" Expected: step-failed (submitReachable) ist auf keiner dlg-*-Ansicht mehr gemeldet. Erlaubt sind nur noch Treffer dark-surface und low-contrast in Dateien aus Task 9.

  • Step 6: Commit
git add src/styles/global.css docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md
git commit -m "Design: KOMPAT-Schicht fuer Altklassen, tote Regeln und !important entfernt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 5: Gerüst, Menü, Logo, Favicon

Files:

  • Create: src/components/Logo.jsx, public/favicon.svg (Kopie von quelle/favicon.svg)
  • Modify:
    • src/App.jsx
    • src/components/Navbar.jsx
    • src/lib/navLinks.js (hot: true am Chat-Unterpunkt)
    • index.html (nur <link rel="icon" href="/favicon.svg" type="image/svg+xml">)
    • src/styles/serverpuls.css (Zeilen 70-74 .side-dot* löschen)
    • src/pages/ProjectsPage.jsx (Wurzel main-content → page)
    • src/pages/AdminPage.jsx (<main className="page admin-page"> → <div className="page admin-page">, zwei Stellen)
  • Delete: src/components/Footer.jsx, src/lib/sidebar.js, tests/unit/sidebar.test.mjs
  • Test: tests/unit/navLinks.test.mjs
  • Modify (Werkzeug): $W/views.mjs bekommt die Ansicht menue-mobil

Interfaces:

  • Consumes: Klassen aus Task 3.

  • Produces:

    • Logo({ size = 24 }) mit aria-hidden="true". Die Pfade kommen aus quelle/app.js, Konstante LOGO.
    • Sidebar({ onNavigate }).
  • Step 1: Test für hot (failing)

In tests/unit/navLinks.test.mjs die Zeile assert.equal(kunden.children.find((c) => c.key === 'chat').hot, true) ergänzen. Die bestehenden deepEqual bleiben.

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/navLinks.test.mjs Expected: FAIL.

  • Step 2: navLinks.js ergänzen, Test grün

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/navLinks.test.mjs Expected: PASS.

  • Step 3: Logo und Favicon anlegen

  • Step 4: Gerüst in App.jsx

AppContent hält menuOpen und baut dieses Gerüst:

<div className={`shell${menuOpen ? ' menu-open' : ''}`}>
  <Sidebar onNavigate={close} />
  <div className="scrim" onClick={close} />
  <div className="main">
    <div className="mobile-bar">
      <Link className="brand" to="/tickets"><Logo />WEBklar</Link>
      <button type="button" className="btn btn-quiet" onClick={open} aria-expanded={menuOpen} aria-controls="menue">Menü</button>
    </div>
    <main id="inhalt"><AppRoutes /></main>
  </div>
</div>
  • Ein Effekt auf location.pathname (nur darauf) schließt die Schublade und ruft window.scrollTo(0, 0) auf.

  • Ein Esc-Listener ist nur bei menuOpen aktiv.

  • Der Ladezustand von ProtectedRoute ist <div className="loading">Wird geladen …</div>.

  • Step 5: Navbar.jsx als Textliste

Aufbau: <aside className="sidebar" id="menue">, darin .brand (Logo und „WEBklar“), <nav className="nav"> mit .nav-item und .sub und am Ende .sidebar-foot.

Element Markup
Pausiert <span className="nav-item paused" aria-disabled="true">Leads<span className="nav-count">pausiert</span></span>
Zähler <span className={nav-count${child.hot ? ' hot' : ''}}>
Serverpuls <span className={nav-dot${dot === 'critical' ? ' critical' : ''}} data-testid="sp-nav-dot" title=…>, nur wenn dot nicht 'ok' ist. Titeltexte wie heute.
Fuß <span className="who text-ellipsis">{user.name || user.email}</span> und <button type="button" className="link" onClick={handleLogout}>Abmelden</button>

Klicks auf Einträge rufen onNavigate() auf.

Entfernt werden:

  • @tabler-Imports und ICONS
  • Einklappen und startsCollapsed

Bleiben:

  • isActive und isLinkActive

  • das Serverpuls-Polling

  • useSidebarCounts

  • Step 6: Altdateien löschen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git rm -q src/components/Footer.jsx src/lib/sidebar.js tests/unit/sidebar.test.mjs && rg -n "Footer|startsCollapsed|lib/sidebar" src tests Expected: keine Treffer.

  • Step 7a: Gate p1 um die Menü-Prüfung ergänzen

In $W/gates.mjs bekommt p1 zusätzlich scoped['icon-svg'] = { views: ['*'], ignore: ['#inhalt *'] }. Der vorhandene Eintrag scoped['overflow-x'] bleibt. Damit prüft icon-svg ab hier Menü, Mobil-Leiste und Login auf allen Ansichten, den Seiteninhalt erst im jeweiligen Paket. Commit im Werkzeug-Repo.

  • Step 7: Ansicht menue-mobil

Bei 390×844 läuft:

  1. Klick auf „Menü“: .shell.menu-open ist da.
  2. Screenshot.
  3. Esc: .menu-open ist weg.
  4. Menü erneut öffnen, Klick auf „Projekte“: Der Pfad ist /projects, und das Menü ist zu.
  • Step 8: Prüfen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t5" --only tickets,kunden,serverpuls,menue-mobil --gate p1 Expected:

  • Exit 0.

  • Im Menü ist kein svg außer dem Logo.

  • Step 9: Commit

git add src/App.jsx src/components/Navbar.jsx src/components/Logo.jsx src/lib/navLinks.js tests/unit/navLinks.test.mjs index.html public/favicon.svg src/styles/serverpuls.css src/pages/ProjectsPage.jsx src/pages/AdminPage.jsx
git commit -m "Design: Geruest mit Textmenue, Schublade fuers Handy, Logo; Footer und Einklappen entfallen" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 6: UI-Bausteine

Files:

  • Create: src/components/ui/tones.js, src/components/ui/Stat.jsx
  • Modify:
    • Button.jsx, Badge.jsx, Card.jsx, PageHeader.jsx, Tabs.jsx, EmptyState.jsx, Field.jsx, Kpi.jsx, StatusPill.jsx, index.js
    • nur Klasse form-control → input bzw. select: AutocompleteInput.jsx, DateTimeInput.jsx, DurationSelect.jsx
  • Test: tests/unit/uiTones.test.mjs

Interfaces:

  • Produces:

    • die Signaturen aus der Übersicht
    • badgeToneClass(tone) → string
    • BUTTON_CLASSES

    Beide liegen in tones.js. Button setzt keinen eigenen Standard-type.

  • Step 1: Failing Test

const { badgeToneClass, BUTTON_CLASSES } = await loadModule('src/components/ui/tones.js')
test('badgeToneClass', () => {
  for (const t of ['muted', 'grey', undefined, 'unbekannt']) assert.equal(badgeToneClass(t), 'badge')
  assert.equal(badgeToneClass('info'), 'badge blue')
  assert.equal(badgeToneClass('blue'), 'badge blue')
  assert.equal(badgeToneClass('ok'), 'badge green')
  assert.equal(badgeToneClass('warn'), 'badge orange')
  assert.equal(badgeToneClass('danger'), 'badge red')
  assert.equal(badgeToneClass('violet'), 'badge violet')
  assert.equal(badgeToneClass('plain'), 'badge plain')
})
test('Button-Varianten', () => {
  assert.deepEqual(BUTTON_CLASSES, { default: 'btn', ghost: 'btn', primary: 'btn btn-primary', quiet: 'btn btn-quiet', danger: 'link-danger' })
})

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/uiTones.test.mjs Expected: FAIL.

  • Step 2: tones.js anlegen, Test grün

  • Step 3: Komponenten umschreiben

Komponente Änderung
Card ohne Titel und ohne Aktionen nur <section>
PageHeader Brotkrumen als Link, wenn to gesetzt ist. Das letzte Element ist Text, das „/“ steht in <span className="faint">.
Tabs active und aria-selected
EmptyState ohne Inline-Styles
Field Hinweis als .hint
Kpi gibt Stat zurück
StatusPill ohne pillBase
  • Step 4: Prüfen

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t6" --only tickets,ticket,kunde-overview,finanzen --gate p1 Expected: Exit 0. Die Status-Badges in tickets zeigen die neuen Töne.

  • Step 5: Commit
git add src/components/ui tests/unit/uiTones.test.mjs
git commit -m "Design: UI-Bausteine rendern die neuen Klassen (Button, Badge, Card, Kopf, Reiter, Stat)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 7: Dialog

Files:

  • Create: src/components/ui/Dialog.jsx, src/lib/dialogKeys.js
  • Modify: src/components/ui/index.js, $W/views.mjs (Ansicht dlg-probe)
  • Test: tests/unit/dialogKeys.test.mjs

Interfaces:

  • Consumes: useDialogFocus(dialogRef) aus src/hooks/useDialogFocus.js, unverändert.

  • Produces:

    • Dialog laut Übersicht
    • shouldCloseOnKey({ key, defaultPrevented }, busy) → boolean
  • Step 1: Failing Test

const { shouldCloseOnKey } = await loadModule('src/lib/dialogKeys.js')
test('Esc schliesst nur ohne Sperre und ohne vorherige Behandlung', () => {
  assert.equal(shouldCloseOnKey({ key: 'Escape', defaultPrevented: false }, false), true)
  assert.equal(shouldCloseOnKey({ key: 'Escape', defaultPrevented: true }, false), false)
  assert.equal(shouldCloseOnKey({ key: 'Escape', defaultPrevented: false }, true), false)
  assert.equal(shouldCloseOnKey({ key: 'Enter', defaultPrevented: false }, false), false)
})

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/dialogKeys.test.mjs Expected: FAIL.

  • Step 2: Implementieren, Test grün

  • Step 3: Dialog.jsx

Teil Umsetzung
Esc keydown-Listener auf document, prüft mit shouldCloseOnKey(e, busy) und ruft dann onClose() auf
Hintergrund Klick ohne Wirkung
Box role="dialog", aria-modal="true", aria-labelledby={titleId} (useId()), ref, tabIndex={-1}
Overlay onKeyDown aus useDialogFocus
× <button type="button" className="dialog-close" aria-label="Schließen" disabled={busy} onClick={onClose}>×</button>
as="form" die Box ist <form onSubmit> ohne noValidate
  • Step 4: dlg-probe

Die Ansicht öffnet /tickets und fügt per page.evaluate einen statischen Klon des Dialog-Markups ein: ein Titel, drei Felder und ein Fuß mit Absende-Knopf. Geprüft wird nur das CSS:

  • Bei 390 px liegt die Box unten.
  • Das Overlay hat Fenstergröße.
  • submitReachable gelingt bei 390×700.

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/shots.sh" p1 "$W/shots/p1-t7" --only dlg-probe --gate p1 Expected: Exit 0.

  • Step 5: Commit
git add src/components/ui/Dialog.jsx src/components/ui/index.js src/lib/dialogKeys.js tests/unit/dialogKeys.test.mjs
git commit -m "Design: Dialog-Baustein (Esc, Sperre waehrend Speichern, Fokus, Bottom-Sheet)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 8: Login

Files:

  • Modify: src/pages/LoginPage.jsx (nur Markup)
  • Delete: src/components/LetterGlitch.jsx

Interfaces:

  • Consumes:

    • useAuth() mit login, mfaPending, sendCode, verifyCode und cancelMfa, unverändert
    • Logo
    • die Klassen .login*, .field, .input, .code-input, .btn-primary.btn-block und .msg
  • Step 1: Markup ersetzen

Grundaufbau: .login > .login-box > <Logo size={40} />.

Schritt E-Mail und Passwort

Element Inhalt
Überschrift h1 „Anmelden“
Unterzeile .sub „Ticketsystem für das WEBklar-Team“
Feld E-Mail Label „E-Mail“, type="email", autoComplete="username", Platzhalter deine@email.com
Feld Passwort Label „Passwort“, type="password", autoComplete="current-password", minLength={8}
Knopf .btn.btn-primary.btn-block „Weiter“, beim Laden „Login läuft...“
Fehler .msg.err mit role="alert"

Schritt Code

Element Inhalt
Überschrift h1 „Code eingeben“
Text die beiden Bestandssätze wörtlich
Hinweis notice als .msg.ok
Feld #mfa-code mit className="input code-input", Label „Code aus der E-Mail“
Knopf „Anmelden“ bzw. „Prüfe…“
Links .login-links mit zwei button.link (type="button"): „Code erneut senden“ und „Abbrechen“

Ohne challengeId erscheint stattdessen „Code senden“ bzw. „Sende…“ und darunter „Abbrechen“.

Fuß: .login-foot „Webklar Ticket-Plattform · © {Jahr}“.

Bleibt unverändert: alle Handler und Zustände.

  • Step 2: LetterGlitch löschen und prüfen

Zusätzlich entfällt im Werkzeug die Ausnahme knownPageError für LetterGlitch in $W/shots.mjs (eigener Commit im Werkzeug-Repo).

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git rm -q src/components/LetterGlitch.jsx && ! rg -n "LetterGlitch" src && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1-t8" --only login,login-code,login-code-senden --gate p1 Expected: Exit 0. Im Bild login-code-senden steht der Knopf „Code senden“.

  • Step 3: Commit
git add src/pages/LoginPage.jsx
git commit -m "Design: neues Login (weiss, Logo, Code-Schritte), Glitch-Hintergrund entfernt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 9: Pflicht-Fixes gegen Unlesbares

Files:

  • Create: src/components/serverpuls/chartColors.js, $W/farben.mjs
  • Modify:
    • CSS: src/styles/chat.css, src/pages/AdminPage.css, src/styles/admin-areas.css, src/styles/serverpuls.css, src/styles/global.css (Bereich Finanzen und Rechtliches)
    • Projekt-Bausteine: src/components/AssignedProjectCard.jsx (Zeilen 27, 82, 102), src/components/PreviewBlock.jsx, src/components/PreviewLinkButton.jsx, src/components/GiteaLinkButton.jsx, src/components/AnalyseRow.jsx (COLORS)
    • Arbeitsblätter: src/components/CreateWorksheetModal.jsx (Zeilen 194, 262), src/components/WorksheetTimesModal.jsx (Zeile 143), src/components/WorksheetList.jsx (nur Farben, siehe Step 4)
    • Serverpuls: src/components/serverpuls/Chart.jsx, src/pages/ServerpulsPage.jsx (Serienfarben)
    • Doku: docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md (Abschnitt „Bewusst offen“)

Interfaces:

  • Produces:

    • CHART laut Übersicht.
    • farben.mjs <worktree> listet je Treffer Datei:Zeile, Literal und Grund:
      • kontrast<3 (Text auf Weiß)
      • dunkel-auf-akzent
      • weiss (#fff, #ffffff, white, rgba(255,255,255,x) als Text- oder Randfarbe außerhalb von .btn-primary und Elementen auf --accent)
      • verlauf (linear-gradient in JSX oder CSS)
  • Step 1: Scan (failing)

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/farben.mjs" "$WT" Expected: eine Liste mit Treffern aller vier Gründe, unter anderem aus WorksheetList.jsx.

  • Step 2: Chat-Variablen

Unter .woms-chat in chat.css:

  • --chat-accent: var(--accent)
  • --chat-accent-hover: var(--accent-hover)
  • --chat-accent-soft: var(--accent-soft)
  • --chat-accent-line: rgba(17,17,17,.14)
  • --chat-accent-text: var(--accent)
  • --chat-on-accent: #ffffff
  • --chat-surface: var(--bg)
  • --chat-line: var(--line-strong)
  • --chat-muted: var(--text-2)

Weitere Regeln:

  • Rand der eigenen Blase: var(--line).

  • .woms-chat .chat-messages { background: var(--bg) }.

  • .woms-chat .chat-composer-note, .woms-chat .chat-tray-item.is-error .chat-file-size und .woms-chat .chat-audio.is-error .chat-audio-time bekommen color: var(--red-fg).

  • .woms-chat .chat-composer-note.is-info bekommt color: var(--text-2).

  • Step 3: Literale umstellen nach fester Zuordnung

Literal Token
#a0aec0, #cbd5e0, #9aa7b4, #e2e8f0 (als Text) var(--text-2)
#34d399, #6ee7b7, #10b981 (als Text) var(--green-fg)
#f87171, #fca5a5, #fecaca, #ef4444 (als Text) var(--red-fg)
#fbbf24, #f59e0b (als Text) var(--orange-fg)
#818cf8, #9d3dfe var(--violet-fg)
#3b82f6, #60a5fa var(--blue-fg)
rgba(255,255,255,x) als Rand oder Linie var(--line)
rgba(255,255,255,x) als Fläche entfällt bzw. wird var(--bg-subtle)
#05251c, #04130d auf Akzent #ffffff
rgba(30,41,59,x) und ähnliche dunkle Flächen var(--bg-subtle)

.admin-primary-button bekommt color: #fff und beim Überfahren background: var(--accent-hover).

  • Step 4: WorksheetList.jsx minimal

Nur die Farben werden geändert:

  • Verläufe und Weiß-Alphas gehen auf Tokens nach der Tabelle oben.
  • Weiße Schrift auf farbigen Köpfen wird var(--text) auf var(--bg-subtle).

Struktur und Klassen bleiben. Den Umbau macht P2.

  • Step 5: Diagrammfarben

chartColors.js wird angelegt. Chart.jsx nutzt CHART.axis, CHART.grid (nur horizontal), CHART.tick und als Standard-Serienfarbe CHART.line. ServerpulsPage setzt:

  • alle Serien CHART.line

  • Spitze CHART.peak mit dash: [4, 4]

  • Füllung CHART.fill

  • Step 6: Scan erneut

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/farben.mjs" "$WT" Expected:

  • Kein Treffer kontrast<3, dunkel-auf-akzent oder weiss.

  • verlauf-Treffer nur dort, wo .btn-primary oder ein Canvas sie braucht.

  • Jeder Rest steht unter „Bewusst offen“ im Dokument mit Zielpaket und gemessenem Kontrast von mindestens 3:1.

  • Step 7: Commit

git add src/components/serverpuls/chartColors.js src/components/serverpuls/Chart.jsx src/pages/ServerpulsPage.jsx src/styles/chat.css src/pages/AdminPage.css src/styles/admin-areas.css src/styles/serverpuls.css src/styles/global.css src/components/AssignedProjectCard.jsx src/components/PreviewBlock.jsx src/components/PreviewLinkButton.jsx src/components/GiteaLinkButton.jsx src/components/AnalyseRow.jsx src/components/CreateWorksheetModal.jsx src/components/WorksheetTimesModal.jsx src/components/WorksheetList.jsx docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md
git commit -m "Design: Pflicht-Fixes gegen helle Schrift und dunkle Flaechen, Diagrammfarben, Chat-Variablen" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 10: Gate P1, Review, Tag

  • Step 1: Rebase

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git rebase origin/test && "$W/check.sh" Expected: Exit 0. Die Testzahl ist 312 − 3 (sidebar) + 1 (statusTone) + 2 (uiTones) + 1 (dialogKeys) = 313. Liegen neue Commits auf test, kann sie höher sein; die Zahl wird notiert.

  • Step 2: Vollständiger Gate-Lauf

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p1 && "$W/shots.sh" p1 "$W/shots/p1" --gate p1 Expected: Exit 0 für alle Ansichten aus P0 plus basis-weiss, menue-mobil und dlg-probe. Alle dlg-* bestehen submitReachable bei 700 px Höhe.

  • Step 3: Bundle-Größe

Die Werte aus der check.sh-Ausgabe werden mit P0 verglichen. Das CSS sollte kleiner sein, und die Schrift fehlt.

  • Step 4: Code-Review (sonnet)

Der Diff 603d08c..HEAD (inklusive P0) wird gegen die Spec geprüft, Abschnitte:

  • „Gestaltungsregeln“
  • „Bausteine“
  • „KOMPAT“
  • Pakete P0 und P1

Wichtige Befunde werden behoben. Danach kommt der Rebase aus Step 1 noch einmal und Step 2 als Kurzlauf mit --only tickets,login.

  • Step 5: Tag und Übergabe

Run: source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && git tag design-p1 && git log --oneline origin/test..design-p1 && git rev-parse design-p1

Übergabe an den Eigentümer:

  • Screenshots aus $W/shots/p1 (Login, Tickets, Kunde, Projekte, Finanzen, Serverpuls, ein Alt-Dialog, Menü mobil)

  • Hinweis zu den neuen Statusfarben für das Team

  • Push-Zeile git -C /home/webklar/apps/ticket.webklar.com/dev-design push origin 'design-p1^{commit}:refs/heads/test'

  • Rücknahme: git revert --no-commit 603d08c..design-p1, Commit und Push durch den Eigentümer

  • Step 6: Nach dem Push (erst, wenn der Eigentümer gepusht hat)

  • Im Log von webklar-ticket-build steht „Deployment completed successfully“.

  • curl -s https://ticket.webklar.com/ | grep -o 'assets/index-[^"]*\.js' stimmt mit dist/index.html des Tags überein.

  • Ein lesender Sichttest im Chrome des Eigentümers: Login-Seite und Ticketliste.