Files
vergabe-teilnahme/workplans/WP-0017-whynot-design-tokens.md
tegwick fbb8def9ce Plan WP-0017: whynot-design adoption (tokens + CSS only)
Add cross-framework analysis to history/ and WP-0017 workplan for the
tokens+CSS phase of adopting ~/whynot-design. Component port deferred until
upstream ships Lit web components and missing atoms (Card, Modal, Input,
Table, Toast).

Decisions captured: vendor (not npm), big-bang swap (not pilot), keep
off-spec red for btn-danger until upstream defines one.
2026-05-23 19:09:18 +02:00

15 KiB
Raw Blame History

id, title, status, phase, created, depends_on
id title status phase created depends_on
WP-0017 whynot-design Adoption — Phase 1 (Tokens + CSS) ready 17-of-n 2026-05-23 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).


id: WP-0017-T01
title: Vendor-Sync-Skript + initiale Vendor-Übernahme
status: todo

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:

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:

.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: todo

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: todo

**`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: todo

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:

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: todo

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: todo

**`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:

## 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.