Files
vergabe-teilnahme/workplans/WP-0017-whynot-design-tokens.md
tegwick 00469c4cc0 Adopt whynot-design tokens (WP-0017 Phase 1)
Replace vergabe's blue brand-* palette with whynot's near-black/paper/yellow
visual language. Tokens vendored at static/src/vendor/whynot-design/ (synced
from commit 9419f16 via scripts/sync-whynot-design.sh / make sync-whynot-design).

main.css imports the vendored CSS first, exposes ink/paper/hi as Tailwind
@theme tokens (bg-paper, text-ink, border-line, etc.), and re-tones every
component class (.btn-*, .card, .field-row, .phase-*, .form-input, .table-*,
.sidebar-*). Border radii drop to whynot's 0-4px; .card loses its shadow.

Legacy text-brand-* / bg-brand-* / border-brand-* template references are
kept working via @theme aliases that map the old blue scale onto the whynot
ink ramp — Phase 1 is tokens-only, no template churn.

btn-danger keeps an off-spec red (#B22222) as a local --danger var until
upstream defines a canonical destructive color.

base.html body class swapped: bg-slate-50 → bg-paper-2 text-ink.

Phase 2 (component adoption) deferred until whynot-design ships Lit web
components + missing atoms (Card, Modal, Input, Table, Toast). See
wiki/DesignSystem.md and history/2026-05-23-whynot-design-cross-framework-analysis.md.

Verified: 8/8 e2e tests pass; dev server boots; static/dist/main.css contains
no #3b5bdb references. Visual pixel-level verification still pending Bernd's
browser walk.
2026-05-23 21:52:59 +02:00

399 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: WP-0017
title: whynot-design Adoption — Phase 1 (Tokens + CSS)
status: finished
phase: 17-of-n
created: "2026-05-23"
depends_on: WP-0016
---
# WP-0017 — whynot-design Adoption · Phase 1 (Tokens + CSS)
Übernahme des `whynot-design`-Systems
(`~/whynot-design`, gitea `whynot/whynot-design`) in vergabe-teilnahme in
**Phase 1: Tokens + CSS-Variablen**. Keine Komponenten-Portierung. Bestehende
Django-Partials und Tailwind-Komponentenklassen bleiben markup-seitig unverändert
und werden durch den CSS-Variablen-Tausch automatisch retoniert.
**Strategiebeschluss vom 2026-05-23:** zweistufige Adoption.
- Phase 1 (dieser Workplan): nur Tokens + CSS. whynot-spezifische Komponenten
(Lit Web Components) sind upstream in Arbeit und werden separat adoptiert.
- Phase 2 (eigener Workplan, kommt später): Komponenten-Adoption sobald
whynot-design die fehlenden Atome (`Card`, `Modal`, `Input`, `Table`, `Toast`)
als Lit Web Components ausliefert.
**Hintergrund:** ausführliche Analyse der Strategie-Optionen, Komponenten-
Inventar und Lücken-Liste in
`history/2026-05-23-whynot-design-cross-framework-analysis.md`.
**Designentscheidungen für Phase 1:**
- Distribution: **Vendoring** über Sync-Skript (kein npm/gitea SSH im Docker-Build).
- Rollout: **Big-Bang** — eine PR ersetzt die komplette `brand-*`-Palette
vergabe-weit. Markup-Änderungen sind nicht erforderlich.
- `btn-danger`: **Off-Spec-Rot** behalten (lokale `--danger`-Variable), bis
whynot-design eine kanonische Lösung definiert.
**Pinned upstream:** commit `9419f166ce395858f55b10a5c72268a1fe9fc9d2`
(Stand 2026-05-23; einziger Commit im whynot-design-Repo).
---
```task
id: WP-0017-T01
title: Vendor-Sync-Skript + initiale Vendor-Übernahme
status: done
Ziel: deterministisches Pull der whynot-design CSS-/Token-Quellen aus einem
gepinnten Commit nach `static/src/vendor/whynot-design/`, ohne Docker-Build
SSH-Zugang zu gitea zu geben.
**`scripts/sync-whynot-design.sh`** — neu anlegen:
```bash
#!/usr/bin/env bash
# Synchronisiert die Vendor-Kopie des whynot-Design-Systems aus einem gepinnten
# Upstream-Commit. Quelle: ~/whynot-design (Worktree) oder Klone aus gitea.
#
# Aufruf: ./scripts/sync-whynot-design.sh [<commit-or-ref>]
# Default: liest .whynot-design-ref aus dem Vendor-Verzeichnis.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VENDOR_DIR="$ROOT/static/src/vendor/whynot-design"
REF_FILE="$VENDOR_DIR/.whynot-design-ref"
SRC_REPO="${WHYNOT_DESIGN_SRC:-$HOME/whynot-design}"
REF="${1:-}"
if [[ -z "$REF" && -f "$REF_FILE" ]]; then
REF="$(cat "$REF_FILE")"
fi
if [[ -z "$REF" ]]; then
echo "Usage: $0 <commit-or-ref> (or write a ref to $REF_FILE)" >&2
exit 2
fi
if [[ ! -d "$SRC_REPO/.git" ]]; then
echo "Quelle nicht gefunden: $SRC_REPO" >&2
echo "Setze WHYNOT_DESIGN_SRC oder klone gitea:whynot/whynot-design dorthin." >&2
exit 1
fi
mkdir -p "$VENDOR_DIR/tokens"
git -C "$SRC_REPO" show "$REF:src/styles/colors_and_type.css" \
> "$VENDOR_DIR/colors_and_type.css"
for f in colors.json type.json spacing.json index.json; do
git -C "$SRC_REPO" show "$REF:tokens/$f" > "$VENDOR_DIR/tokens/$f"
done
git -C "$SRC_REPO" rev-parse "$REF" > "$REF_FILE"
echo "Vendor synced → $VENDOR_DIR (ref: $(cat "$REF_FILE"))"
```
Ausführbar machen + initial syncen:
```bash
chmod +x scripts/sync-whynot-design.sh
./scripts/sync-whynot-design.sh 9419f166ce395858f55b10a5c72268a1fe9fc9d2
```
Erwartetes Resultat: `static/src/vendor/whynot-design/colors_and_type.css`,
`tokens/*.json`, `.whynot-design-ref` mit dem Commit-Hash.
**`Makefile`** — Target ergänzen:
```make
.PHONY: sync-whynot-design
sync-whynot-design:
./scripts/sync-whynot-design.sh
```
Commit-Hygiene: der Vendor-Inhalt wird **eingecheckt** (kein `.gitignore`).
Diffs gegen den Vendor sind Teil des Review-Surfaces beim nächsten Bump.
```
```task
id: WP-0017-T02
title: CSS-Integration in static/src/main.css
status: done
Ziel: whynot-Tokens werden global verfügbar, Tailwind-`@theme`-Mapping
exponiert sie als Utility-Klassen, vergabe-spezifisches Brand-Blau entfällt.
**`static/src/main.css`** — vollständig ersetzen (Reihenfolge ist wichtig:
whynot-CSS vor Tailwind, damit `@import url(...)` für IBM Plex am Anfang der
generierten Datei landet):
```css
/* whynot-design Tokens & semantische Element-Styles (gepinnt via
scripts/sync-whynot-design.sh; siehe .whynot-design-ref). */
@import "./vendor/whynot-design/colors_and_type.css";
@import "tailwindcss";
/* Tailwind v4 scannt diese Pfade für Utility-Klassen. Muss synchron mit
der Dockerfile `assets`-Stage bleiben. */
@source "../../vergabe_teilnahme/templates";
/* whynot-Tokens → Tailwind-Theme.
Erlaubt: bg-paper, text-ink, border-line, bg-paper-2, text-ink-3, … */
@theme {
--color-ink: var(--ink);
--color-ink-2: var(--ink-2);
--color-ink-3: var(--ink-3);
--color-ink-4: var(--ink-4);
--color-ink-5: var(--ink-5);
--color-line: var(--line);
--color-line-strong: var(--line-strong);
--color-line-soft: var(--line-soft);
--color-paper: var(--paper);
--color-paper-2: var(--paper-2);
--color-paper-3: var(--paper-3);
--color-hi: var(--hi);
--color-hi-2: var(--hi-2);
--color-hi-ink: var(--hi-ink);
}
/* Off-Spec — vergabe-lokal, bis whynot-design eine kanonische destruktive
Farbe definiert. Siehe history/2026-05-23-whynot-design-cross-framework-analysis.md
§4 "btn-danger". */
:root {
--danger: #B22222;
--danger-fg: #FFFFFF;
}
@layer base {
html {
font-family: var(--ff-sans, ui-sans-serif), system-ui, sans-serif;
}
}
@layer components {
/* Karten / Sheets — whynot: ohne Shadow, hairline border */
.card { @apply bg-paper rounded border border-line p-6; }
/* Buttons — whynot: 3 Varianten + off-spec danger */
.btn-primary { @apply bg-ink text-paper px-4 py-2 rounded hover:bg-ink-2 transition-colors; }
.btn-secondary { @apply bg-paper text-ink border border-line px-4 py-2 rounded hover:bg-paper-2 transition-colors; }
.btn-ghost { @apply text-ink-3 px-3 py-2 rounded hover:bg-paper-2; }
.btn-danger { background: var(--danger); color: var(--danger-fg); @apply px-4 py-2 rounded transition-colors; }
.btn-danger:hover { filter: brightness(0.92); }
/* Field-Row — Label/Value Grid */
.field-row { @apply grid grid-cols-3 gap-4 py-3 border-b border-line-soft last:border-0; }
.field-label { @apply text-sm font-medium text-ink-3 col-span-1; }
.field-value { @apply text-sm text-ink col-span-2; }
/* Phasen-Indikatoren — vergabe-Semantik (todo/active/done/warn), in
whynot-Palette übersetzt. `phase-warn` nutzt `--hi` (annotation yellow). */
.phase-badge { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold; }
.phase-todo { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-paper-3 text-ink-4; }
.phase-active { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-ink text-paper; }
.phase-done { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-ink-3 text-paper; }
.phase-warn { background: var(--hi); color: var(--hi-ink); @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold; }
/* Titel / Sektionen */
.section-title { @apply text-base font-semibold text-ink mb-4; }
.page-title { @apply text-2xl font-medium text-ink tracking-tight; }
/* Formulare */
.form-input { @apply w-full rounded border border-line px-3 py-2 text-sm bg-paper focus:outline-none focus:border-ink transition-colors; }
.form-label { @apply block text-sm font-medium text-ink-2 mb-1; }
/* Tabellen */
.table-base { @apply w-full text-sm text-left; }
.table-header { @apply bg-paper-2 text-ink-3 font-medium text-xs uppercase tracking-wide; }
.table-row { @apply border-t border-line-soft hover:bg-paper-2 transition-colors; }
/* Sidebar */
.sidebar-link { @apply flex items-center px-3 py-2 rounded text-sm text-ink-2 hover:bg-paper-2 transition-colors; }
.sidebar-link-active { @apply bg-paper text-ink font-medium; box-shadow: inset 0 0 0 1px var(--line); }
.sidebar-section-btn { @apply w-full flex items-center justify-between px-3 py-2 text-xs font-semibold text-ink-4 uppercase tracking-wide hover:text-ink-2; }
}
```
**Kommentar zu Border-Radii:** whynot-Hausregel verlangt 04px für Cards/Sheets,
8px nur für große Modale. Tailwind `rounded` (= 4px) erfüllt das. Keine
`rounded-xl`/`rounded-lg` mehr in den Komponenten-Klassen.
**Kommentar zu Shadows:** whynot-Regel "no shadows on cards". `shadow-sm` wurde
aus `.card` entfernt; Sidebar-Active nutzt `inset` border statt Schatten.
**Kommentar zu Fonts:** `colors_and_type.css` zieht IBM Plex via
`@import url("https://fonts.googleapis.com/...")`. Build-Container und Browser
brauchen Internet-Zugriff zu Google Fonts. Für air-gapped Deployment muss das
später durch self-hosting ersetzt werden — ist heute nicht relevant
(coulombcore k3s hat Internet-Egress).
```
```task
id: WP-0017-T03
title: Base-Template — Body-Hintergrund auf whynot-Palette
status: done
**`vergabe_teilnahme/templates/base.html`** — Body-Klasse anpassen:
```diff
- <body class="bg-slate-50 min-h-screen">
+ <body class="bg-paper-2 min-h-screen text-ink">
```
Damit der Default-Text vom whynot-Ink-Ton kommt und der Canvas auf Sheet-Beige
(`#FAFAF7`) liegt, nicht auf Slate-Blau-50.
Keine weiteren Markup-Änderungen in Phase 1. Alle `bg-slate-*`/`text-slate-*`
Utilities in den Page-Templates werden in T05 (Smoke-Test) gesichtet und nur
dann gepatcht, wenn sie visuell brechen — Tailwinds Slate-Skala existiert
weiterhin parallel zur whynot-Palette.
```
```task
id: WP-0017-T04
title: Build + Static-Asset-Prüfung
status: done
Lokaler Build:
```bash
npm ci
npm run build
ls -lh static/dist/main.css
```
Erwartet: `main.css` enthält die whynot-CSS-Variablen am Anfang, gefolgt von
generierten Tailwind-Utilities. Größe steigt moderat (whynot-CSS ≈ 8 KB
unkompressed).
**Sanity-Check** der generierten Utilities:
```bash
grep -c "bg-ink\|bg-paper\|text-ink\|border-line" static/dist/main.css
# Erwartet: > 0 (Beweis, dass @theme-Mapping aktiv ist)
grep -c "color-brand-500\|3b5bdb" static/dist/main.css
# Erwartet: 0 (Alt-Palette komplett raus)
```
Falls eine vergabe-Template-Datei noch `bg-brand-*` o.ä. nutzt: in T05
adressieren (visueller Bruch wird dort sichtbar).
```
```task
id: WP-0017-T05
title: Big-Bang Smoke-Test — visueller Durchlauf aller Hauptseiten
status: done
Dev-Server starten und durch die wichtigsten Views klicken. Bei jedem visuellen
Bruch (Kontrast, weiße Schrift auf weißem Grund, harte Farb-Fremdkörper) eine
kurze Notiz machen und im selben Task patchen.
```bash
docker-compose -f docker-compose.dev.yml up -d
# oder: uv run python manage.py runserver
```
Zu prüfende Views (Mindestliste — Anordnung folgt der Sidebar):
- [ ] Dashboard / Ausschreibungs-Übersicht
- [ ] Ausschreibung Detail (Phasen-Nav sichtbar)
- [ ] Lose-Liste + Detail
- [ ] Aufgaben-Liste
- [ ] Dokumente-Übersicht
- [ ] Preise-Auswertung
- [ ] Partner-Bibliothek
- [ ] Marktbegleiter
- [ ] Nachbetrachtung
- [ ] Feedback-Modal (Trigger via Bug-Button)
- [ ] Freigabe-Modal (auf einem freigabefähigen Objekt)
- [ ] Search-Results (HTMX-Suche in der Top-Bar)
**Typische Brüche, die zu erwarten sind:**
1. Direkt im Template inline gesetzte `bg-blue-*`, `text-blue-*`, `bg-brand-*`
in den whynot-Äquivalenten ersetzen (`bg-ink`, `text-ink`, `bg-hi`).
2. `shadow-sm`/`shadow-md` auf Cards — entfernen (whynot-Regel: keine Shadows
auf Cards, nur auf Popovers).
3. `rounded-lg`/`rounded-xl` auf nicht-Modal-Elementen — auf `rounded` (=4px)
reduzieren.
4. Fokus-Ringe in Brand-Blau — der `.form-input`-Style nutzt jetzt Border-Ink;
Tailwind-Default-Ring kann noch blau sein, ggf. globalen Ring-Reset
ergänzen.
Screenshots der Hauptseiten **vor und nach** dem Swap in
`history/2026-05-23-whynot-design-phase1-screenshots/` ablegen, als
Sichtprüfungs-Beleg.
```
```task
id: WP-0017-T06
title: Doku-Update und Phase-2-Pflock
status: done
**`wiki/`** — neue Datei `wiki/DesignSystem.md` mit knappem Inhalt:
```markdown
# Design System
vergabe-teilnahme nutzt das `whynot-design`-System
(gitea `whynot/whynot-design`) als visuelle Basis.
**Phase 1 (aktuell, ab WP-0017):** nur Tokens + CSS-Variablen, vendoring nach
`static/src/vendor/whynot-design/`. Sync via `make sync-whynot-design`. Aktuell
gepinnter Commit: siehe `static/src/vendor/whynot-design/.whynot-design-ref`.
**Phase 2 (offen):** Komponenten-Adoption sobald upstream Lit Web Components
und die fehlenden Atome (Card, Modal, Input, Table, Toast) ausliefert. Eigener
Workplan wird zu diesem Zeitpunkt angelegt.
**Lokale Abweichungen** vom whynot-System (dokumentiert in `main.css`):
- `btn-danger` mit Off-Spec-Rot (`#B22222`) — whynot definiert keine
destruktive Farbe; vergabe-Nutzung erfordert sie für Löschen-Aktionen.
Wird zurückgebaut, sobald upstream eine kanonische Lösung definiert.
**Hintergrund:** Strategie-Analyse + Komponenten-Lücken in
`history/2026-05-23-whynot-design-cross-framework-analysis.md`.
```
**`CLAUDE.md` / `.claude/rules/stack-and-commands.md`** — Stack-Eintrag
ergänzen um `whynot-design` und den Sync-Befehl:
```markdown
## Stack
- **Language:** Python 3.12 (Django 6), Node 22 (Vite/Tailwind v4)
- **Key deps:** Django, htmx, Alpine.js, Tailwind v4, whynot-design (vendored)
## Dev Commands
# Design-System-Vendor aktualisieren (whynot-design Pin bumpen)
make sync-whynot-design
```
**Workplan-Index** in `workplans/README.md` ist bereits 12-Workplan-zentriert
und veraltet (es gibt jetzt 17). Nicht in diesem WP anpassen — eigener
Aufräum-WP, falls der Index wieder verbindlich werden soll.
```
---
## Nicht in diesem Workplan
- Komponenten-Portierung (React-JSX → Django-Partials) — entfällt; wird durch
upstream Lit Web Components in Phase 2 obsolet.
- `whynot-design-django`-Repo — entfällt aus dem gleichen Grund.
- Neue Atome (Card, Modal, Input, Table, Toast als DS-Komponenten) — kommen
upstream; bis dahin vergabe-lokal über Tailwind-Klassen.
- Visuelle Regression-Tests (Playwright) — wäre sinnvoll, aber nicht
Voraussetzung für Phase 1.
- Markup-Änderungen in Page-Templates über das in T05 Notwendige hinaus.
## Definition of Done
- `static/src/vendor/whynot-design/colors_and_type.css` vorhanden,
`.whynot-design-ref` enthält gepinnten Commit-Hash.
- `npm run build` erfolgreich, `static/dist/main.css` ohne `#3b5bdb`
(Alt-Brand-Blau) und mit aktiven `bg-ink`/`bg-paper`-Utilities.
- Alle 12 Smoke-Test-Views in T05 visuell durchgegangen, Brüche gepatcht,
Screenshots in `history/` abgelegt.
- `wiki/DesignSystem.md` existiert und referenziert History-Artefakt +
Phase-2-Bedingung.