5a444ec8f2
Vier Quick-Tasks mit Plan, Bericht und Verifikation; Browser-Nachweis der Web-Teile lokal erbracht (Kacheln #284a7b bei #0057b8, Proxy-Symbol trotz Zertifikatsfehler, Direktbild bei interner Adresse, Sortierung ueber Reload). Offen: CI-Beweis des Desktop-Skips nach diesem Push, Windows-VM-Probe der Client-Aenderungen mit dem CI-Paket. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
174 lines
25 KiB
Markdown
174 lines
25 KiB
Markdown
---
|
||
phase: quick-260917-jdf
|
||
plan: 01
|
||
type: execute
|
||
wave: 1
|
||
depends_on: []
|
||
autonomous: true
|
||
requirements: [QUICK-260917-JDF]
|
||
|
||
files_modified:
|
||
- apps/web/src/components/brand/brand.ts
|
||
- apps/web/src/components/brand/brand.test.ts
|
||
- apps/web/src/components/brand/tessera-logo.tsx
|
||
- apps/web/src/components/brand/tessera-logo.test.tsx
|
||
- CHANGELOG.md
|
||
|
||
estimate:
|
||
tokens: 24000
|
||
raw_tokens: 24000
|
||
tasks: 2
|
||
confidence: low
|
||
|
||
must_haves:
|
||
truths:
|
||
- "In `LogoMark` (tessera-logo.tsx) tragen die vier achsenparallelen Kacheln den Inline-Style `fill: BRAND_OLIVE_FILL`, also `color-mix(in oklab, var(--primary, #ffed00) 54%, #363636)`, und behalten zusaetzlich das Praesentationsattribut `fill` mit BRAND_OLIVE (`#9c9440`) als Rueckfall fuer Browser ohne `color-mix()`. Die gedrehte Signalkachel bleibt bei `var(--primary, #ffed00)`, die Grundplatte bei BRAND_PLATE. Damit folgt das ganze T der per `applyAccentColor` gesetzten Akzentfarbe (QUICK-260917-JDF)."
|
||
- "Kalibrierung: Bei `--primary: #ffed00` ergibt die Mischung exakt `#9c9440` (Rechnung sRGB→OKLab→sRGB nach CSS Color 4, Abweichung 0/0/0 je Kanal; Toleranz laut Auftrag ≤ 2). Beim CSS-Standardwert `--primary: oklch(0.91 0.19 102)` aus globals.css (≈ #fbe405, gilt auf der Anmeldeseite und fuer Nutzer ohne persoenliche Akzentfarbe) ergibt sich `#9a903f` (−2/−4/−1 neben #9c9440) — visuell nicht unterscheidbar; Anmeldeseite und Nutzer ohne Akzentfarbe sehen die Bildmarke unveraendert, ohne neuen Prop und ohne Sonderpfad."
|
||
- "Die Mischparameter stehen genau einmal im Quellcode: `BRAND_OLIVE_MIX = { primaryShare: 54, mixWith: '#363636' } as const` in brand.ts; der CSS-String `BRAND_OLIVE_FILL` wird daraus und aus BRAND_YELLOW gebildet. brand.test.ts rechnet die Mischung aus DENSELBEN Konstanten nach — keine zweite Zahlenquelle."
|
||
- "brand.test.ts (neu, Vitest) belegt: (a) Mischung von BRAND_YELLOW mit BRAND_OLIVE_MIX liegt je RGB-Kanal ≤ 2 Einheiten neben BRAND_OLIVE; (b) `BRAND_OLIVE_FILL` passt zum Muster `color-mix(in oklab, var(--primary, #rrggbb) N%, #rrggbb)` und traegt genau die Zahlen aus BRAND_OLIVE_MIX; (c) das Mischgrau ist in OKLab neutral (|a| und |b| < 1e-4), der Farbton der Akzentfarbe bleibt also erhalten; (d) Nebenpruefung: fuer `oklch(0.91 0.19 102)` ≤ 4 Einheiten neben BRAND_OLIVE, fuer #0057b8 und #ffffff ist der abgeleitete Ton dunkler (OKLab-L kleiner), fuer #000000 heller (dokumentiertes Kippen)."
|
||
- "tessera-logo.test.tsx prueft: fuenf Kacheln; genau eine mit `style.fill === \\`var(--primary, ${BRAND_YELLOW})\\`` und `transform`; genau vier mit `style.fill === BRAND_OLIVE_FILL`, `getAttribute('fill') === BRAND_OLIVE` und ohne `transform`. jsdom 29.1.1 haelt `color-mix(...)` unveraendert in `element.style.fill` (vom Planer per Probe bestaetigt)."
|
||
- "Unveraendert: `apps/web/src/app/icon.svg` (Favicon, statisch, weiter #9c9440), `apps/web/src/app/globals.css`, `apps/web/src/app/(auth)/login/page.tsx`, die Tauri-Icons. Keine JS-Farbrechnung zur Laufzeit, kein neuer Prop an `TesseraLogo`."
|
||
- "`pnpm --filter @tessera/web exec vitest run` (Grundstand 63 Dateien / 417 Tests, danach 64 Dateien) und `pnpm --filter @tessera/web type-check` enden gruen. Kein Docker-Build, kein `git push`, keine `.planning/`-Commits, keine Dateien ausserhalb von files_modified."
|
||
- "CHANGELOG.md, `## Unveröffentlicht` → `### Geändert`: genau ein neuer Stichpunkt zur Bildmarke. Der Abschnitt ist nach der 1.2.0-Freigabe leer; die Ueberschrift wird angelegt, falls sie fehlt (parallele Quick-Tasks koennen sie inzwischen angelegt haben — dann nur den Stichpunkt anhaengen). Nur Zeilen ergaenzt, keine geloescht."
|
||
- "Zwei Commits: `feat(brand): …` (Task 1) und `docs: …` (Task 2). Das SUMMARY vermerkt das Kippen bei sehr dunklen Akzentfarben (OKLab-L unter ≈ 0.33, z. B. Schwarz → Kacheln heller als die Signalkachel, hingenommen) und den Befund zum CSS-Standardwert von `--primary`."
|
||
artifacts:
|
||
- "apps/web/src/components/brand/brand.ts — `BRAND_OLIVE_MIX`, `BRAND_OLIVE_FILL` (neu), Kalibrierungskommentar mit Zahlen, angepasster Kommentar zu BRAND_OLIVE"
|
||
- "apps/web/src/components/brand/brand.test.ts — Kalibrierungstest mit eigener sRGB↔OKLab-Rechnung (neu)"
|
||
- "apps/web/src/components/brand/tessera-logo.tsx — vier Kacheln mit Inline-Style BRAND_OLIVE_FILL plus Rueckfall-Attribut, korrigierter Kommentar, JSDoc"
|
||
- "apps/web/src/components/brand/tessera-logo.test.tsx — angepasster Kacheltest"
|
||
- "CHANGELOG.md — ein Stichpunkt unter Unveröffentlicht → Geändert"
|
||
- ".planning/quick/260917-jdf-bildmarke-ganzes-t-uebernimmt-die-akzent/260917-jdf-oklab-kalibrierung.cjs — Referenzrechnung des Planers (Eingabe fuer den Executor; nicht Teil des Produkts, wird nicht vom Executor committet)"
|
||
key_links:
|
||
- "`applyAccentColor` (auth-store.ts Z. 21-43) setzt `--primary` inline auf `document.documentElement`; beide Kachelsorten lesen den Token per `var(--primary, …)` — die vier Kacheln innerhalb von `color-mix()`. Das ist weiterhin die einzige Verdrahtung zwischen Akzentfarbe und Bildmarke: kein Prop, kein Store-Zugriff, keine JS-Farbrechnung."
|
||
- "globals.css definiert `--primary: oklch(0.91 0.19 102)` in `:root` (Z. 66) UND `.dark` (Z. 93) — der Token ist in der laufenden App immer definiert. Der `var()`-Rueckfall `#ffed00` greift daher nie (nur ohne geladenes Stylesheet); die Aussage im Kommentar tessera-logo.tsx Z. 75-77 („ohne angemeldeten Nutzer … ist der Token nicht definiert“) ist falsch und wird in Task 1 korrigiert. Kalibrierziel bleibt BRAND_YELLOW als dokumentierte Markenfarbe; Abweichung beim CSS-Standard −2/−4/−1."
|
||
- "Inline-Style vor Praesentationsattribut: versteht der Browser `color-mix()`, gilt der Inline-Style; versteht er es nicht, wird die Deklaration verworfen und das Attribut (#9c9440) gilt. Ohne Attribut waere die Kachel dort schwarz (SVG-Standardfuellung) — deshalb beides."
|
||
- "Mischung in `oklab` statt `oklch`: mit neutralem Mischgrau ist das Ergebnis identisch (Farbton bleibt, Chroma und Helligkeit skalieren linear), aber ohne Abhaengigkeit von der Regel fuer den „powerless hue“ achromatischer Farben in polaren Raeumen. #363636 hat in OKLab a ≈ 3e-11, b ≈ 1e-8."
|
||
- "Die Zaehl-Gates im `<verify>` von Task 1 zaehlen Quelltextzeilen (`grep -c`); Kommentare in tessera-logo.tsx duerfen die JSX-Attributschreibweise der Kacheln deshalb nicht zitieren."
|
||
---
|
||
|
||
<objective>
|
||
Die Tessera-Bildmarke soll als Ganzes der persoenlichen Akzentfarbe folgen. Seit quick-260917-gsh nimmt nur die gedrehte Signalkachel `--primary` an; die vier achsenparallelen Kacheln haben weiter das feste Oliv `#9c9440`. Jetzt bekommen diese vier Kacheln einen aus der Akzentfarbe abgeleiteten dunkleren, gedeckten Ton derselben Farbe — im selben Verhaeltnis wie heute Gelb `#ffed00` → Oliv `#9c9440` (OKLCH: L 0.931 → 0.656, C 0.197 → 0.106, Farbton 104° unveraendert).
|
||
|
||
Mechanismus: reines CSS ohne Laufzeit-Farbrechnung. Die vier Kacheln bekommen den Inline-Style `fill: color-mix(in oklab, var(--primary, #ffed00) 54%, #363636)`; das bisherige Praesentationsattribut `fill` mit `#9c9440` bleibt als Rueckfall fuer Browser ohne `color-mix()` stehen (Inline-Style gewinnt, sobald er verstanden wird). Die Mischparameter (54 %, #363636) sind vom Planer kalibriert: sie liefern fuer `#ffed00` exakt `#9c9440` (Abweichung 0 je Kanal; Referenzrechnung `260917-jdf-oklab-kalibrierung.cjs` im Quick-Ordner). Alle Markenzahlen bleiben in brand.ts (Konstanten `BRAND_OLIVE_MIX`, `BRAND_OLIVE_FILL`); ein neuer Vitest `brand.test.ts` rechnet die Mischung aus genau diesen Konstanten nach.
|
||
|
||
Befund des Planers, den der Executor im SUMMARY festhaelt: `--primary` ist ueber globals.css immer definiert (`oklch(0.91 0.19 102)` ≈ `#fbe405`), der `var()`-Rueckfall `#ffed00` greift also nie. Fuer Nutzer ohne persoenliche Akzentfarbe (und auf der Anmeldeseite) ist die Signalkachel deshalb bereits seit 260917-gsh `#fbe405` statt `#ffed00`, und die vier Kacheln werden `#9a903f` statt `#9c9440` — jeweils wenige Einheiten, nicht sichtbar. Die Anmeldeseite bindet die Bildmarke ohne feste Farben ein (`login/page.tsx` Z. 54-62 und 71, nur `BRAND_YELLOW` als Panel-Hintergrund) und braucht keine Sonderbehandlung. Bei sehr dunklen Akzentfarben (OKLab-L unter ≈ 0.33, z. B. Schwarz) wird der abgeleitete Ton heller statt dunkler — laut Auftrag hingenommen, im SUMMARY vermerken.
|
||
|
||
Purpose: Nutzer mit eigener Akzentfarbe sehen ein einheitliches T in ihrer Farbe statt einer farbigen Kachel neben vier olivfarbenen Fremdkoerpern (QUICK-260917-JDF).
|
||
Output: brand.ts mit Mischkonstanten + Kalibrierungskommentar, brand.test.ts (neu), angepasste tessera-logo.tsx + Test, ein CHANGELOG-Stichpunkt, zwei Commits.
|
||
</objective>
|
||
|
||
<execution_context>
|
||
@~/.claude/gsd-core/workflows/execute-plan.md
|
||
@~/.claude/gsd-core/templates/summary.md
|
||
</execution_context>
|
||
|
||
<context>
|
||
@/home/vicolab/projects/tessera-ctl/CLAUDE.md
|
||
@/home/vicolab/projects/tessera-ctl/apps/web/src/components/brand/brand.ts
|
||
@/home/vicolab/projects/tessera-ctl/apps/web/src/components/brand/tessera-logo.tsx
|
||
@/home/vicolab/projects/tessera-ctl/apps/web/src/components/brand/tessera-logo.test.tsx
|
||
@/home/vicolab/projects/tessera-ctl/.planning/quick/260917-jdf-bildmarke-ganzes-t-uebernimmt-die-akzent/260917-jdf-oklab-kalibrierung.cjs
|
||
@/home/vicolab/projects/tessera-ctl/apps/web/src/lib/stores/auth-store.ts
|
||
</context>
|
||
|
||
<tasks>
|
||
|
||
<task type="tracer" tdd="true">
|
||
<name>Task 1: Vier Kacheln in abgeleiteter Akzentfarbe — Konstanten, Logo, Kalibrierungstest, Kacheltest</name>
|
||
<files>apps/web/src/components/brand/brand.ts, apps/web/src/components/brand/brand.test.ts, apps/web/src/components/brand/tessera-logo.tsx, apps/web/src/components/brand/tessera-logo.test.tsx</files>
|
||
<read_first>
|
||
- .planning/quick/260917-jdf-bildmarke-ganzes-t-uebernimmt-die-akzent/260917-jdf-oklab-kalibrierung.cjs — einmal ausfuehren (`node …cjs`), Ausgabe lesen; die Funktionen `srgbToLinear`, `linearToSrgb`, `rgbToOklab`, `oklabToRgb`, `lchToLab`, `hexToRgb`, `rgbToHex` werden 1:1 in brand.test.ts uebernommen (TypeScript-Typen ergaenzen)
|
||
- apps/web/src/components/brand/brand.ts (komplett, 23 Zeilen)
|
||
- apps/web/src/components/brand/tessera-logo.tsx Z. 1-4 (Import), Z. 69-90 (Kachelgruppe mit Kommentar), Z. 95-103 (JSDoc)
|
||
- apps/web/src/components/brand/tessera-logo.test.tsx Z. 1-6 (Imports), Z. 97-119 (Kacheltest)
|
||
- apps/web/src/app/globals.css Z. 66 und Z. 93 (`--primary: oklch(0.91 0.19 102)` — nur lesen, nicht aendern)
|
||
- apps/web/src/lib/color.test.ts Z. 1-10 (Kopfkommentar-Stil fuer reine Tests)
|
||
</read_first>
|
||
<behavior>
|
||
brand.test.ts (`describe('Markenfarben — abgeleiteter Olivton (BRAND_OLIVE_MIX)')`), Rechnung sRGB→OKLab→sRGB im Testfile selbst, Helfer `mixInOklab(labA, share, hexB)` = `color-mix(in oklab, A share%, B)`:
|
||
- Kalibrierung: `mixInOklab(oklab(BRAND_YELLOW), BRAND_OLIVE_MIX.primaryShare, BRAND_OLIVE_MIX.mixWith)` liegt je RGB-Kanal hoechstens 2 Einheiten neben BRAND_OLIVE (erwartet: 0/0/0, Ergebnis `#9c9440`).
|
||
- Einheitliche Quelle: `BRAND_OLIVE_FILL` matcht `/^color-mix\(in oklab, var\(--primary, (#[0-9a-f]{6})\) (\d+)%, (#[0-9a-f]{6})\)$/`; Gruppe 1 === BRAND_YELLOW, Number(Gruppe 2) === BRAND_OLIVE_MIX.primaryShare, Gruppe 3 === BRAND_OLIVE_MIX.mixWith.
|
||
- Neutralitaet: OKLab-`a` und `b` von BRAND_OLIVE_MIX.mixWith sind betragsmaessig < 1e-4 (Farbton der Akzentfarbe bleibt erhalten).
|
||
- CSS-Standard: `mixInOklab(lchToLab([0.91, 0.19, 102]), …)` liegt je Kanal hoechstens 4 Einheiten neben BRAND_OLIVE (erwartet `#9a903f`, −2/−4/−1). Der OKLCH-Wert ist der `:root`-Standard von `--primary` aus globals.css Z. 66 — als Literal mit Kommentar im Test; das `<verify>` sichert, dass globals.css ihn noch enthaelt.
|
||
- Nebenpruefung Helligkeit (OKLab-L des Ergebnisses gegen L der Quelle): #0057b8 → dunkler (erwartet `#284a7b`), #ffffff → dunkler und neutral (erwartet `#9c9c9c`, |a|,|b| < 1e-3), #000000 → HELLER (erwartet `#0c0c0c`; dokumentiertes Kippen bei sehr dunklen Akzentfarben).
|
||
tessera-logo.test.tsx — der Test „renders exactly five tiles; only the rotated one …“ (Z. 97-119) wird ersetzt durch „renders exactly five tiles; the rotated one takes the accent token, the other four the derived olive tone with the fixed olive as fallback“:
|
||
- `mark.querySelectorAll('g rect')` hat Laenge 5.
|
||
- Genau eine Kachel mit `(tile as SVGRectElement).style.fill === \`var(--primary, ${BRAND_YELLOW})\``; sie hat `transform`; keine andere Kachel hat `transform`; keine Kachel hat `getAttribute('fill') === BRAND_YELLOW`.
|
||
- Genau vier Kacheln mit `style.fill === BRAND_OLIVE_FILL`; jede davon hat `getAttribute('fill') === BRAND_OLIVE` und kein `transform`.
|
||
- `BRAND_OLIVE_FILL` enthaelt `var(--primary` (Zusicherung, dass die vier Kacheln wirklich am Token haengen).
|
||
Alle uebrigen Tests beider Dateien bleiben unveraendert und gruen.
|
||
</behavior>
|
||
<action>
|
||
RED zuerst: brand.test.ts anlegen (scheitert, weil `BRAND_OLIVE_MIX`/`BRAND_OLIVE_FILL` fehlen) und den Kacheltest in tessera-logo.test.tsx umschreiben (scheitert, weil die vier Kacheln keinen Inline-Style haben); beide rot sehen, dann GREEN.
|
||
|
||
**1. `brand.ts`.** Werte von BRAND_YELLOW, BRAND_OLIVE, BRAND_PLATE unveraendert lassen (login/page.tsx und account-settings-form.tsx importieren BRAND_YELLOW weiterhin). Ergaenzen:
|
||
- Kommentar zu `BRAND_OLIVE` (Z. 19) erweitern: Olivton der vier achsenparallelen Kacheln bei Standard-Gelb; in der Bildmarke seit quick-260917-jdf Kalibrierziel und Rueckfall (Praesentationsattribut fuer Browser ohne `color-mix()`), die eigentliche Fuellung leitet `BRAND_OLIVE_FILL` aus der Akzentfarbe ab; `apps/web/src/app/icon.svg` (Favicon) und die daraus erzeugten Tauri-Icons verwenden den Wert weiterhin fest.
|
||
- Neue exportierte Konstante `BRAND_OLIVE_MIX = { primaryShare: 54, mixWith: '#363636' } as const` — Mischanteil der Akzentfarbe in Prozent und neutrales Mischgrau. Deutscher Kommentar mit der Kalibrierung: gesucht war `color-mix(in oklab, #ffed00 P%, #GRAU)` = `#9c9440`; Rechnung sRGB→OKLab→sRGB nach CSS Color 4 (Referenz: `260917-jdf-oklab-kalibrierung.cjs` im Quick-Ordner, Nachweis in brand.test.ts); Ergebnis 54 % / #363636 → `#9c9440` exakt (0/0/0); OKLCH-Verhaeltnis Gelb→Oliv: L 0.931→0.656, C 0.197→0.106, Farbton 104° gleich. Warum `oklab` statt `oklch`: mit neutralem Grau identisches Ergebnis (Farbton bleibt, Chroma und L skalieren linear), aber ohne Abhaengigkeit von der Sonderregel fuer den Farbton achromatischer Farben in polaren Raeumen. Verhalten fuer andere Akzentfarben festhalten: CSS-Standard `oklch(0.91 0.19 102)` → `#9a903f` (−2/−4/−1), #ffffff → neutrales Grau `#9c9c9c`, #0057b8 → `#284a7b`; Akzentfarben dunkler als das Mischgrau (OKLab-L < 0.333, z. B. Schwarz → `#0c0c0c`) werden heller statt dunkler — bewusst hingenommen, kein Schutzmechanismus.
|
||
- Neue exportierte Konstante `BRAND_OLIVE_FILL`: Vorlage-String `color-mix(in oklab, var(--primary, ${BRAND_YELLOW}) ${BRAND_OLIVE_MIX.primaryShare}%, ${BRAND_OLIVE_MIX.mixWith})` — die einzige Stelle, an der der CSS-Ausdruck gebildet wird. Kurzer Kommentar: Inline-`fill` der vier Kacheln in tessera-logo.tsx; `--primary` setzt `applyAccentColor` (auth-store.ts), der CSS-Standard steht in globals.css, der `var()`-Rueckfall greift nur ohne geladenes Stylesheet.
|
||
- Kopfkommentar (Z. 1-9) um einen Satz ergaenzen: auch die Ableitungsregel fuer den Olivton lebt hier.
|
||
|
||
**2. `tessera-logo.tsx`.** Import um `BRAND_OLIVE_FILL` erweitern (`BRAND_OLIVE` bleibt importiert — Rueckfall-Attribut). An jeder der vier achsenparallelen Kacheln (Z. 70, 71, 88, 89) das Praesentationsattribut `fill` mit `BRAND_OLIVE` STEHEN LASSEN und zusaetzlich `style={{ fill: BRAND_OLIVE_FILL }}` setzen — an allen vier identisch, jede Kachel darf dafuer mehrzeilig werden. Den Kommentar Z. 72-78 zu einem Kommentar ueber die ganze Kachelgruppe umschreiben (vor dem `<g>` oder als erstes Kind): alle fuenf Kacheln folgen `--primary` — die gedrehte direkt, die vier anderen als abgeleiteter dunklerer Ton (`BRAND_OLIVE_FILL`, Kalibrierung in brand.ts); `var()`/`color-mix()` sind in SVG-Praesentationsattributen nicht zuverlaessig, im Inline-Style schon; das Praesentationsattribut mit dem festen Oliv bleibt als Rueckfall, weil ein Browser ohne `color-mix()` die Inline-Deklaration verwirft und die Kachel sonst schwarz (SVG-Standard) wuerde; `--primary` wird von `applyAccentColor()` in auth-store.ts gesetzt und hat in globals.css immer einen Standardwert (`oklch(0.91 0.19 102)`, Markengelb) — die bisherige Aussage, ohne angemeldeten Nutzer sei der Token nicht definiert, ist zu streichen. Kommentare in Prosa halten: die JSX-Attributschreibweise der Kacheln nicht zitieren, weil die Zaehl-Gates im verify Quelltextzeilen zaehlen. Gedrehte Kachel (Z. 79-87), Grundplatte, `plateOutline`, Props und `horizontal`-Variante nicht anfassen. JSDoc Z. 101-102 anpassen: die gesamte Bildmarke folgt der persoenlichen Akzentfarbe — Signalkachel = Akzentfarbe, vier Kacheln = daraus abgeleiteter dunklerer Ton (siehe brand.ts).
|
||
|
||
**3. `brand.test.ts` (neu).** Kopfkommentar im Stil von color.test.ts (Zweck: Kalibrierung des abgeleiteten Olivtons, quick-260917-jdf; die Rechnung entspricht `color-mix(in oklab, …)` nach CSS Color 4). Die Umrechnungsfunktionen aus `260917-jdf-oklab-kalibrierung.cjs` mit denselben Matrixzahlen als typisierte lokale Funktionen im Test (keine Abhaengigkeit, kein neues Modul unter src — die Rechnung wird ausschliesslich im Test gebraucht). Imports: `BRAND_OLIVE, BRAND_OLIVE_FILL, BRAND_OLIVE_MIX, BRAND_YELLOW` aus `./brand`. Faelle exakt wie in `<behavior>`; erwartete Hex-Werte als Literale mit Kommentar (Ergebnis der Referenzrechnung), Toleranzen als Zahlen (2 bzw. 4 Einheiten je Kanal) — der Kanalvergleich ueber eine kleine Hilfsfunktion `maxChannelDiff(hexA, hexB)`.
|
||
|
||
**4. `tessera-logo.test.tsx`.** Import Z. 3 um `BRAND_OLIVE_FILL` erweitern; Test Z. 97-119 gemaess `<behavior>` ersetzen. `style.fill` bleibt die klarere Zusicherung gegenueber `getAttribute('style')`; jsdom 29.1.1 haelt `color-mix(in oklab, var(--primary, #ffed00) 54%, #363636)` zeichengenau in `style.fill` (Probe des Planers).
|
||
|
||
Nicht anfassen: globals.css, icon.svg, login/page.tsx, auth-store.ts, Tauri-Icons. Kein neuer Prop an `TesseraLogo`, keine JS-Farbrechnung ausserhalb des Tests.
|
||
</action>
|
||
<verify>
|
||
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web exec vitest run src/components/brand && pnpm --filter @tessera/web type-check && test "$(grep -c 'style={{ fill: BRAND_OLIVE_FILL }}' apps/web/src/components/brand/tessera-logo.tsx)" = "4" && test "$(grep -c 'fill={BRAND_OLIVE}' apps/web/src/components/brand/tessera-logo.tsx)" = "4" && test "$(grep -cF 'var(--primary, ${BRAND_YELLOW})' apps/web/src/components/brand/tessera-logo.tsx)" = "1" && grep -q 'primaryShare: 54' apps/web/src/components/brand/brand.ts && grep -q "mixWith: '#363636'" apps/web/src/components/brand/brand.ts && test "$(grep -c -- '--primary: oklch(0.91 0.19 102)' apps/web/src/app/globals.css)" = "2" && git diff --quiet HEAD -- apps/web/src/app/globals.css apps/web/src/app/icon.svg 'apps/web/src/app/(auth)/login/page.tsx' apps/web/src/lib/stores/auth-store.ts && echo TASK1-OK</automated>
|
||
</verify>
|
||
<done>brand.test.ts und tessera-logo.test.tsx gruen (Kalibrierung 0/0/0 bei #ffed00, CSS-Standard ≤ 4, Neutralitaet, Grenzfaelle; fuenf Kacheln mit 1× Token-Fuellung und 4× abgeleiteter Fuellung plus Rueckfall-Attribut), Typpruefung gruen, in tessera-logo.tsx genau vier Inline-Fuellungen mit BRAND_OLIVE_FILL, vier Rueckfall-Attribute und weiterhin genau eine `var(--primary, …)`-Fuellung, brand.ts traegt 54 / #363636 genau in den Konstanten, globals.css/icon.svg/login/page.tsx/auth-store.ts unveraendert. Commit `feat(brand): ganzes T der Bildmarke übernimmt die Akzentfarbe – Kacheln als abgeleiteter dunklerer Ton` (nur die vier Dateien dieses Tasks).</done>
|
||
</task>
|
||
|
||
<task type="auto">
|
||
<name>Task 2: CHANGELOG — ein Stichpunkt, Gesamtlauf</name>
|
||
<files>CHANGELOG.md</files>
|
||
<action>
|
||
In `CHANGELOG.md` unter `## Unveröffentlicht` (Z. 5; der Abschnitt ist nach der 1.2.0-Freigabe leer, die naechste Ueberschrift ist `## 1.2.0 – 2026-09-17`) den Stichpunkt
|
||
|
||
`- Tessera-Bildmarke: das ganze T übernimmt die persönliche Akzentfarbe (die vier Kacheln in einem dunkleren Ton derselben Farbe)`
|
||
|
||
unter `### Geändert` eintragen. Vorher pruefen, ob eine parallele Quick-Aufgabe die Ueberschrift inzwischen angelegt hat: Existiert `### Geändert` zwischen `## Unveröffentlicht` und `## 1.2.0`, den Stichpunkt als letzte Zeile dieses Blocks anhaengen. Fehlt sie, den Block `### Geändert` + Leerzeile + Stichpunkt anlegen — in der Reihenfolge des Bestands (Neu → Geändert → Entfernt → Behoben): nach einem vorhandenen `### Neu`-Block, sonst direkt nach `## Unveröffentlicht` und der folgenden Leerzeile; vor der Ueberschrift `## 1.2.0` bleibt eine Leerzeile. Stil wie im Bestand: echte Umlaute, kein Punkt am Ende, kein Fliesstext, keine anderen Zeilen anfassen oder loeschen (die Aufgaben zum Favoriten-Widget und zur CI ergaenzen dieselbe Datei). Danach den vollstaendigen Web-Testlauf und die Typpruefung als Abschlussgate ausfuehren. Das `<verify>` VOR dem Commit laufen lassen — der Diff-Zaehler misst den Arbeitsbaum gegen HEAD (Task 1 ist committet, CHANGELOG.md noch nicht).
|
||
</action>
|
||
<verify>
|
||
<automated>cd /home/vicolab/projects/tessera-ctl && SECT="$(sed -n '/^## Unveröffentlicht/,/^## 1\.2\.0/p' CHANGELOG.md)" && test "$(printf '%s\n' "$SECT" | grep -c '^### Geändert$')" = "1" && test "$(printf '%s\n' "$SECT" | sed -n '/^### Geändert$/,/^##/p' | grep -c 'das ganze T übernimmt die persönliche Akzentfarbe')" = "1" && test "$(grep -c 'das ganze T übernimmt die persönliche Akzentfarbe' CHANGELOG.md)" = "1" && NUMSTAT=$(git diff --numstat HEAD -- CHANGELOG.md) && test -n "$NUMSTAT" && test "$(printf '%s' "$NUMSTAT" | awk '{print $2}')" = "0" && pnpm --filter @tessera/web exec vitest run && pnpm --filter @tessera/web type-check && echo TASK2-OK</automated>
|
||
</verify>
|
||
<done>Der Stichpunkt steht genau einmal in der Datei, und zwar im Block `### Geändert` des Abschnitts „Unveröffentlicht“ (Ueberschrift genau einmal in diesem Abschnitt); der CHANGELOG-Diff gegen HEAD enthaelt keine geloeschten Zeilen; kompletter Web-Testlauf (Grundstand 63 Dateien / 417 Tests plus brand.test.ts) und Typpruefung gruen. Commit `docs: CHANGELOG — Bildmarke übernimmt die Akzentfarbe als Ganzes` (nur CHANGELOG.md).</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<threat_model>
|
||
## Trust Boundaries
|
||
|
||
| Boundary | Description |
|
||
|----------|-------------|
|
||
| API-Antwort → `--primary` → `color-mix()` | Der gespeicherte Akzentwert des Nutzers wird als CSS-Token gesetzt und in einem CSS-Farbausdruck verwendet; reine Darstellung, keine Eingabe in diesem Task |
|
||
|
||
## STRIDE Threat Register
|
||
|
||
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
||
|-----------|----------|-----------|----------|-------------|-----------------|
|
||
| T-jdf-01 | Tampering | `--primary` innerhalb von `BRAND_OLIVE_FILL` | low | accept | Wert stammt aus der API-Antwort desselben Nutzers, serverseitig auf `/^#[0-9a-fA-F]{6}$/` beschraenkt (unveraendert seit 260917-gsh); CSS-Farbfunktionen fuehren nichts aus, ein ungueltiger Wert laesst die Deklaration verfallen → Rueckfall-Attribut `#9c9440` |
|
||
| T-jdf-02 | Denial of Service | Bildmarke in Browsern ohne `color-mix()` | low | mitigate | Praesentationsattribut `fill` mit BRAND_OLIVE bleibt an allen vier Kacheln (sonst schwarze Kacheln = unlesbares T) |
|
||
| T-jdf-SC | Tampering | npm/pnpm installs | low | accept | Keine Paketinstallationen in diesem Plan (Rechnung im Test ohne Abhaengigkeit; die Referenzrechnung des Planers laeuft mit blossem Node) |
|
||
</threat_model>
|
||
|
||
<verification>
|
||
- `pnpm --filter @tessera/web exec vitest run` gruen (inkl. neuem `src/components/brand/brand.test.ts` und angepasstem `tessera-logo.test.tsx`).
|
||
- `pnpm --filter @tessera/web type-check` gruen.
|
||
- `git status --porcelain` nach den zwei Commits: nur `.planning/`-Dateien offen; keine Datei ausserhalb von `files_modified` veraendert (insbesondere globals.css, icon.svg, login/page.tsx unveraendert).
|
||
- Browser-Nachweis (Orchestrator per Playwright, nicht Teil dieses Plans): Einstellungen → Konto → Akzentfarbe `#0057b8` speichern → in Kopfzeile und Seitenleiste wird das ganze T blau, die vier Kacheln erkennbar dunkler als die Signalkachel (erwartet ≈ `#284a7b`); Zuruecksetzen → gelb/oliv wie vorher; Abmelden → Anmeldeseite gelb/oliv wie vorher (Signalkachel ≈ `#fbe405`, Kacheln ≈ `#9a903f` — beides ununterscheidbar vom bisherigen Bild). Messung ueber `getComputedStyle(rect).fill` der Kacheln, nicht per `fetch` aus der Seite.
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
- Vier Kacheln mit Inline-Fuellung `BRAND_OLIVE_FILL` plus Rueckfall-Attribut; Signalkachel und Grundplatte unveraendert; kein neuer Prop, keine JS-Farbrechnung.
|
||
- Mischparameter 54 % / #363636 einmalig in `BRAND_OLIVE_MIX`, CSS-String daraus gebildet, Kalibrierung im Test aus denselben Konstanten belegt (0/0/0 bei #ffed00, ≤ 4 beim CSS-Standard, Neutralitaet des Mischgraus, Grenzfaelle dokumentiert).
|
||
- Ein CHANGELOG-Stichpunkt, zwei Commits, alle Gates gruen; SUMMARY nennt das Kippen bei sehr dunklen Akzentfarben und den Befund, dass der `var()`-Rueckfall wegen des CSS-Standardwerts nie greift.
|
||
</success_criteria>
|
||
|
||
<output>
|
||
Create `.planning/quick/260917-jdf-bildmarke-ganzes-t-uebernimmt-die-akzent/260917-jdf-SUMMARY.md` when done
|
||
</output>
|