--- phase: quick-260917-gsh plan: 01 subsystem: ui tags: [react, nextjs, tailwind, vitest, next-intl, svg, css-custom-properties] requires: [] provides: - "normalizeHexColor() (apps/web/src/lib/color.ts) — reine Funktion, normalisiert Hex-Farbeingaben auf #rrggbb" - "Einstellungen → Konto: Hex-Textfeld neben dem Akzentfarb-Waehler, bidirektional synchron" - "LogoMark: gedrehte Signalkachel folgt der persoenlichen Akzentfarbe (--primary) statt fester gelber Fuellung" affects: [settings, brand, i18n] actuals: tokens: 5987 tasks: 3 commits: 3 plan_head_before: f6eda20 tech-stack: added: [] patterns: - "SVG-Praesentationsattribute (fill=...) loesen var() nicht zuverlaessig auf; Inline-Style (style={{ fill: 'var(--token, fallback)' }}) tut es" key-files: created: - apps/web/src/lib/color.ts - apps/web/src/lib/color.test.ts - apps/web/src/components/settings/account-settings-form.test.tsx modified: - apps/web/src/components/settings/account-settings-form.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json - apps/web/src/components/brand/tessera-logo.tsx - apps/web/src/components/brand/tessera-logo.test.tsx - apps/web/src/components/brand/brand.ts - CHANGELOG.md key-decisions: - "onBlur des Hex-Textfelds normalisiert auf die kanonische Form (Kleinbuchstaben, expandierte Kurzform) statt den Rohtext stehen zu lassen — damit bleibt z. B. FFED00 nicht dauerhaft in Grossbuchstaben im Feld." - "accentColor (Farbwaehler-Zustand) bleibt die einzige Quelle, die je an und die Speichern-Action geht; der Rohtext lebt getrennt in hexInput, damit ein ungueltiger Zwischenstand nie auf #000000 zurueckfallen laesst." - "Bildmarke: Inline-Style statt fill-Attribut, weil jsdom/Browser var() in SVG-Praesentationsattributen nicht zuverlaessig aufloesen; BRAND_YELLOW bleibt als textueller Rueckfallwert im Style-String erhalten." patterns-established: - "Formular-Sync zwischen einer strukturierten Eingabe (color-Picker) und einer Freitext-Alternative: getrennter Rohtext-Zustand + abgeleitete Gueltigkeit, kanonischer Wert wird nur bei Gueltigkeit in den strukturierten Zustand uebernommen." requirements-completed: [QUICK-260917-GSH] coverage: - id: D1 description: "Hex-Textfeld neben dem Farbwaehler, vorbelegt, normalisiert Eingaben, synchron mit dem Farbwaehler in beide Richtungen, Fehlerzustand + Speichersperre bei ungueltigem Wert, Reset setzt beides zurueck" requirement: "QUICK-260917-GSH" verification: - kind: unit ref: "apps/web/src/components/settings/account-settings-form.test.tsx#AccountSettingsForm — Akzentfarbe Hex-Eingabe (7 Tests)" status: pass human_judgment: true rationale: "Visuelle/funktionale Bedienprobe im Browser (Farbwaehler-Reaktion, roter Rand, Speichern-Sperre) ist laut Plan Aufgabe des Orchestrators, nicht Teil dieses Plans." - id: D2 description: "normalizeHexColor() als reine Funktion mit neun Testfaellen (gueltig/ungueltig, Kurzform, Leerraum, Gross-/Kleinschreibung)" requirement: "QUICK-260917-GSH" verification: - kind: unit ref: "apps/web/src/lib/color.test.ts#normalizeHexColor (9 Tests)" status: pass human_judgment: false - id: D3 description: "Gedrehte Signalkachel der Bildmarke folgt der Akzentfarbe (--primary) mit Markengelb als Rueckfall; vier Olivkacheln und Grundplatte unveraendert" requirement: "QUICK-260917-GSH" verification: - kind: unit ref: "apps/web/src/components/brand/tessera-logo.test.tsx#renders exactly five tiles; only the rotated one is filled from the accent token with the brand yellow as fallback" status: pass human_judgment: true rationale: "Sichtbarer Farbwechsel in Kopfzeile/Seitenleiste/Anmeldeseite ist eine visuelle Bedienprobe im Browser — laut Plan Aufgabe des Orchestrators, nicht Teil dieses Plans." duration: ~20min completed: 2026-09-17 status: complete --- # Quick Task 260917-gsh: Akzentfarbe — Hex-Eingabe und Bildmarke in Akzentfarbe Summary **Hex-Textfeld neben dem Akzentfarb-Waehler (normalizeHexColor() als reine Funktion) plus gedrehte Logokachel, die per `var(--primary, BRAND_YELLOW)` der persoenlichen Akzentfarbe folgt** ## Performance - **Duration:** ~20 min - **Completed:** 2026-09-17T10:16:32Z - **Tasks:** 3/3 - **Files modified:** 10 (3 neu, 7 geaendert) ## Accomplishments - `normalizeHexColor()` (apps/web/src/lib/color.ts) normalisiert Hex-Eingaben (fuehrendes `#` optional, 3-stellige Kurzform expandiert, Leerraum getrimmt, kleingeschrieben) — 9 Testfaelle gruen - Kontoformular: Hex-Textfeld ersetzt den reinen Anzeige-``, bidirektional synchron mit dem Farbwaehler, Fehlerzustand (`aria-invalid`, roter Rand, Fehlertext) sperrt „Farbe speichern" - `LogoMark` (Bildmarke): gedrehte Signalkachel per Inline-Style `fill: var(--primary, #ffed00)` statt festem `fill`-Attribut — folgt jetzt der Akzentfarbe in Kopfzeile, Seitenleiste und leerem Dashboard, Markengelb bleibt Rueckfall vor der Anmeldung - i18n de/en vollstaendig (`settings.account.accentColorHex`, `accentColorHexInvalid`), CHANGELOG mit zwei Stichpunkten ## Task Commits Each task was committed atomically: 1. **Task 1: Hex-Eingabe — Normalisierung, Formular-Sync, i18n, Tests** - `795c6a4` (feat) 2. **Task 2: Bildmarke — gedrehte Kachel in Akzentfarbe, Test und Kommentare** - `1601d97` (feat) 3. **Task 3: CHANGELOG — zwei Stichpunkte, Gesamtlauf** - `db478e0` (docs) **Plan metadata:** wird vom Orchestrator nach diesem SUMMARY committet. _Task 1 (`type="tracer" tdd="true"`) und Task 2 (`tdd="true"`) folgten RED→GREEN: Testdateien zuerst angelegt und rot gesehen (fehlendes Modul bzw. fehlender Inline-Style), dann die Implementierung ergaenzt._ ## Files Created/Modified - `apps/web/src/lib/color.ts` - `normalizeHexColor(input): string | null`, reine Funktion, keine Abhaengigkeiten - `apps/web/src/lib/color.test.ts` - 9 Testfaelle (gueltig/ungueltig, Kurzform, Leerraum, Gross-/Kleinschreibung) - `apps/web/src/components/settings/account-settings-form.tsx` - Hex-Textfeld, `hexInput`-Zustand, Sync mit Farbwaehler, Speichern-Sperre bei ungueltigem Text - `apps/web/src/components/settings/account-settings-form.test.tsx` - 7 Testfaelle (Vorbelegung, Sync in beide Richtungen, Fehlerzustand, Blur-Normalisierung, Reset, Speichern) - `apps/web/src/messages/de.json`, `en.json` - `settings.account.accentColorHex`, `accentColorHexInvalid` - `apps/web/src/components/brand/tessera-logo.tsx` - gedrehte Kachel mit `style={{ fill: 'var(--primary, ...)' }}`, JSDoc-Ergaenzung, deutscher Kommentar zur Begruendung - `apps/web/src/components/brand/tessera-logo.test.tsx` - Kacheltest umgeschrieben (Inline-Style-Fuellung statt `fill`-Attribut) - `apps/web/src/components/brand/brand.ts` - Kommentar zu `BRAND_YELLOW` als Rueckfallwert - `CHANGELOG.md` - je ein Stichpunkt unter „Neu" und „Geändert" ## Decisions Made - onBlur des Hex-Textfelds normalisiert auf die kanonische Form (Kleinbuchstaben, expandierte Kurzform) — Ermessensentscheidung des Planers, uebernommen wie im Plan vorgesehen. - `accentColor` bleibt die einzige Quelle fuer Farbwaehler und Speichern-Action; der Rohtext lebt getrennt in `hexInput`, damit ein ungueltiger Zwischenstand `` nie auf `#000000` zurueckfallen laesst. - Inline-Style statt `fill`-Praesentationsattribut fuer die Logokachel, weil `var()` in SVG-Praesentationsattributen nicht zuverlaessig aufgeloest wird. ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered None. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Browser-Nachweis (Orchestrator, nicht Teil dieses Plans): Einstellungen → Konto, `FFED00`/`#fe0` tippen → Farbwaehler folgt; `#ggg` → roter Rand, Speichern gesperrt; Farbe speichern → Kachel der Bildmarke wechselt sofort; Zuruecksetzen → gelb; Anmeldeseite nach Abmelden → Kachel gelb. - Kein Blocker fuer weitere Arbeit — Formular- und Bildmarken-Tests laufen unveraendert weiter mit dem restlichen Web-Testlauf (381/381 gruen, Typpruefung sauber). --- *Quick Task: 260917-gsh* *Completed: 2026-09-17* ## Self-Check: PASSED All 11 claimed files found on disk; all 3 task commits (795c6a4, 1601d97, db478e0) found in git history.