From 17a8355f27d0b48eafd52d42b534ab79419486e5 Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 7 Sep 2026 11:19:16 +0200 Subject: [PATCH] docs(quick-260907-fgv): Plan fuer festen Einbau von Tessera-Logo und Favicon Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq --- .../260907-fgv-PLAN.md | 449 ++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 .planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-PLAN.md diff --git a/.planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-PLAN.md b/.planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-PLAN.md new file mode 100644 index 0000000..7762dae --- /dev/null +++ b/.planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-PLAN.md @@ -0,0 +1,449 @@ +--- +quick_id: 260907-fgv +slug: tessera-logo-und-favicon-fest-einbauen-b +date: 2026-09-07 +status: planned +relates_to: 01-foundation-portal-shell + +phase: quick-260907-fgv +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - apps/web/src/app/icon.svg + - apps/web/src/app/apple-icon.png + - apps/web/src/components/brand/brand.ts + - apps/web/src/components/brand/tessera-logo.tsx + - apps/web/src/components/brand/tessera-logo.test.tsx + - apps/web/src/messages/de.json + - apps/web/src/messages/en.json + - apps/web/src/components/layout/header.tsx + - apps/web/src/components/layout/sidebar.tsx + - apps/web/src/components/layout/sidebar.test.tsx + - apps/web/src/app/(auth)/login/page.tsx + - apps/web/src/app/(auth)/reset-password/page.tsx + - apps/web/src/app/(auth)/reset-password/[token]/page.tsx + - apps/web/src/components/settings/account-settings-form.tsx +autonomous: false +requirements: [QUICK-260907-FGV] + +estimate: + tokens: 55000 + raw_tokens: 55000 + tasks: 3 + confidence: low + +must_haves: + truths: + - "Der Browser-Tab zeigt die Bildmarke statt des Standard-Globus — in hellem und in dunklem Erscheinungsbild." + - "Kopfleiste, Seitenleisten-Schublade, Anmeldeseite (breite und schmale Ansicht) und beide Passwort-Seiten zeigen dieselbe Bildmarke aus derselben Komponente." + - "Der Schriftzug bleibt in beiden Erscheinungsbildern lesbar, weil er echter HTML-Text in der Vordergrundfarbe der Oberflaeche ist und kein eingebetteter Text mit fest eingebackener Farbe." + - "Die dunkle Platte der Bildmarke hebt sich im dunklen Erscheinungsbild vom Hintergrund ab, weil sie dort eine helle Haarlinie als Kontur bekommt; im hellen Erscheinungsbild ist die Kontur unsichtbar." + - "Auf dem gelben Anmelde-Panel bleibt die Bildmarke ohne Kontur, weil dieses Panel unabhaengig vom Erscheinungsbild immer eine helle Flaeche ist." + - "Der Gelbwert der Marke ist im gesamten Web-Quellcode genau einmal notiert; Anmelde-Panel und Bildmarke lesen ihn aus dieser einen Konstante." + - "Das gelieferte Doppel favicon.svg / tessera-logo.svg wird nur einmal in das Repository uebernommen — der Byte-Vergleich hat beide als identisch nachgewiesen." + - "Es entsteht keine Hochlade-Maske, kein Einstellungsfeld, keine Datenbankspalte und keine Mandantenabfrage fuer das Logo." + artifacts: + - apps/web/src/app/icon.svg + - apps/web/src/app/apple-icon.png + - apps/web/src/components/brand/brand.ts + - apps/web/src/components/brand/tessera-logo.tsx + - apps/web/src/components/brand/tessera-logo.test.tsx + key_links: + - "BRAND_YELLOW in brand.ts -> Signalkachel der Bildmarke UND Hintergrund des Anmelde-Panels UND Vorgabewert der persoenlichen Akzentfarbe: eine Konstante, drei Nutzer" + - "TesseraLogo -> header.tsx, sidebar.tsx, login/page.tsx (zweimal), reset-password/page.tsx, reset-password/[token]/page.tsx: die eine Stelle, an der eine spaetere Mandanten-Marke ansetzt" + - "common.brand.logoAlt in de.json UND en.json -> aria-label der Bildmarke; fehlt der Schluessel in einer Sprache, liest ein Screenreader den Schluesselnamen vor" + - "apps/web/src/app/icon.svg -> dateibasierte Metadaten von Next.js: die Datei MUSS unter src/app/ liegen, in public/ waere sie wirkungslos, weil layout.tsx kein icons-Feld setzt" +--- + + +Das gelieferte Tessera-Logo und das Favicon fest in die Oberflaeche einbauen — und +zwar so, dass die spaetere Wunschfunktion "Administrator gestaltet das Erscheinungsbild +je Mandant" genau eine Datei anfassen muss. + +Purpose: Die Plattform traegt bis heute an jeder Marken-Stelle nur das Wort "Tessera" +als Text, dazu auf der Anmeldeseite ein handgezeichnetes Vier-Quadrate-Platzhalter-Icon. +Das gelieferte Logo ersetzt beides. + +Output: Eine zentrale Komponente `TesseraLogo`, eine Farbkonstanten-Datei, zwei +Icon-Dateien fuer den Browser-Tab, sechs umgestellte Verwendungsstellen. + +## Die gelieferten Dateien und was mit ihnen geschieht + +Der Ordner `user-files/logo/` ist ueber `.gitignore:32` vom Repository ausgeschlossen. +Nichts darf von dort aus referenziert werden — was gebraucht wird, wird kopiert. + +| Datei | Verwendung | +|---|---| +| `favicon.svg` | wird nach `apps/web/src/app/icon.svg` kopiert (Browser-Tab) | +| `tessera-logo.svg` | wird NICHT kopiert — `diff` weist sie als byteweise identisch zu `favicon.svg` aus | +| `apple-touch-icon.png` | wird nach `apps/web/src/app/apple-icon.png` kopiert | +| `tessera-logo-schriftzug.svg` | wird NICHT kopiert — Begruendung unten | +| `favicon-16.png`, `favicon-32.png` | werden NICHT kopiert — jeder von Tessera unterstuetzte Browser zeigt SVG-Favicons | +| `tessera-logo-512.png` | wird NICHT kopiert — es gibt keine Web-App-Manifest-Datei, die sie braeuchte | + +## Vier Entscheidungen, die vorab getroffen wurden + +**1. Der Schriftzug wird nicht als Bild ausgeliefert, sondern als echter Text neben der +Bildmarke gesetzt.** Zwei unabhaengige Gruende. Erstens die Farbe: in +`tessera-logo-schriftzug.svg` steht die Textfarbe zweimal fest eingebacken als dunkler +Ton; auf dem dunklen Erscheinungsbild der Anwendung waere der Schriftzug unsichtbar. +Zweitens die Schrift: die Datei verlangt die Schriftart Archivo in Schnitt 900 — die +Anwendung laedt ueber `--font-sans` in `globals.css:33` ausschliesslich Inter, Archivo +ist nirgends eingebunden. Der Browser wuerde also ohnehin eine andere Schrift einsetzen, +mit anderen Buchstabenbreiten, in einem starr auf 420x100 festgelegten Zeichenbereich — +der Schriftzug wuerde je nach Rechner ueberlaufen oder verloren in der Flaeche stehen. +Beides faellt weg, wenn "Tessera" normaler HTML-Text in der Hausschrift ist: er erbt die +Vordergrundfarbe, er bricht nie, und er kommt aus `common.appName`, ist also uebersetzbar. +Die Alternative — je eine helle und eine dunkle Variante der Bilddatei ausliefern — waere +zwei Dateien, ein Theme-Hook und ein Umschaltflackern beim Seitenaufbau; sie loest das +Schriftproblem ausserdem gar nicht. + +**2. Die dunkle Platte der Bildmarke bekommt im dunklen Erscheinungsbild eine Kontur.** +Nachgerechnet, nicht vermutet: die Platte der Marke ist ein sehr dunkles Grau, der +dunkle Anwendungshintergrund `oklch(0.23 0.01 260)` entspricht `#1a1d22`, die dunkle +Seitenleiste `oklch(0.20 0.01 260)` entspricht `#13161b`. Platte und Hintergrund sind +damit praktisch derselbe Ton — die Platte verschwindet, die fuenf Kacheln wuerden frei +schweben. Die Marke selbst bleibt trotzdem unangetastet: die Platte erhaelt lediglich +ueber eine `dark:`-Utility-Klasse eine halbtransparente helle Haarlinie als Kontur. Im +hellen Erscheinungsbild ist die Kontur durchsichtig, dort aendert sich also nichts. Das +ist dieselbe Behandlung, die die Anwendung ohnehin fuer dunkle Flaechen auf dunklem +Grund verwendet. Auf dem gelben Anmelde-Panel wird die Kontur per Eigenschaft +abgeschaltet, weil dieses Panel unabhaengig vom gewaehlten Erscheinungsbild gelb bleibt +und die Platte dort von sich aus maximalen Kontrast hat. Fuer die Icon-Datei im +Browser-Tab bleibt die Platte ohne Kontur: die Tab-Leiste ist keine Flaeche dieser +Anwendung, und die Wiedererkennung traegt dort ohnehin die helle Signalkachel. + +**3. Der Gelbwert steht kuenftig genau einmal im Quellcode.** Er kommt heute dreimal vor: +als Hintergrund-Utility des Anmelde-Panels, im Kopfkommentar derselben Datei und als +Vorgabewert der persoenlichen Akzentfarbe in `account-settings-form.tsx:14`. Kuenftig +steht er nur noch in `apps/web/src/components/brand/brand.ts`; die drei Stellen lesen ihn +von dort. Bewusst KEINE neue CSS-Variable in `globals.css`: die Auswahl der Akzentfarbe +laeuft ueber ein ``, das zwingend einen echten Farbwert braucht und +mit einer CSS-Variablen nichts anfangen kann. Eine TypeScript-Konstante bedient alle drei +Stellen, eine CSS-Variable koennte es nicht. Der vorhandene Token `--primary` bleibt +unberuehrt: er ist ein rechnerisch leicht abweichender Ton (`#fbe405` gegen den Gelbwert +der Marke) und steuert Schaltflaechen, Fokusringe und Aktivmarkierungen — ihn auf den +Markenwert zu ziehen waere eine Umfaerbung der gesamten Anwendung und ist nicht Teil +dieser Aufgabe. + +**4. Der Browser-Tab wird ueber dateibasierte Metadaten verdrahtet, nicht ueber +`metadata.icons`.** Next.js liegt hier in Version 15.5.19 mit App Router vor; eine Datei +`src/app/icon.svg` erzeugt den ``-Eintrag von selbst. Damit bleibt +`layout.tsx` unberuehrt — weniger Code, weniger Fehlerquellen. Das leere Verzeichnis +`apps/web/public/` bleibt leer; dort abgelegte Icons wuerden ohne zusaetzlichen Eintrag +in `layout.tsx` nicht gefunden. + +## Zwei sichtbare Aenderungen, die ueber "Logo einbauen" hinausgehen und deshalb ausdruecklich zur Abnahme gestellt werden + +- **Der Schriftzug ist kuenftig nicht mehr gelb.** Kopfleiste und Seitenleiste setzen + "Tessera" heute in der Primaerfarbe, also Gelb auf weissem Grund — schwer lesbar. Das + gelieferte Logo setzt den Schriftzug dunkel. Die Komponente folgt dem Logo und nutzt + die Vordergrundfarbe der Oberflaeche. +- **Auf dem gelben Anmelde-Panel steht die Bildmarke kuenftig OBEN.** Heute steht dort + Schriftzug, Zeile darunter, Platzhalter-Icon ganz unten. Das gelieferte Logo ist als + Bildmarke-zuerst aufgebaut; die Komponente uebernimmt diese Reihenfolge. Der bestehende + Werbesatz bleibt unveraendert darunter stehen. + +Beides steht im Abnahmeschritt und kann dort zurueckgewiesen werden. + +## Was ausdruecklich NICHT passiert + +Keine Hochlade-Maske, keine Einstellungsseite, kein Datenbankfeld, keine +Mandantenabfrage fuer das Logo. Der Wunsch "ein Administrator soll das Erscheinungsbild +je Mandant anpassen koennen" wird als eigener Backlog-Punkt festgehalten und vom +Orchestrator eingereicht — nicht in diesem Plan gebaut. + + + +@~/.claude/gsd-core/workflows/execute-plan.md +@~/.claude/gsd-core/templates/summary.md + + + +@CLAUDE.md +@.planning/STATE.md +@apps/web/src/app/layout.tsx +@apps/web/src/app/globals.css +@apps/web/src/components/layout/header.tsx +@apps/web/src/components/layout/sidebar.tsx +@apps/web/src/components/layout/sidebar.test.tsx +@apps/web/src/app/(auth)/login/page.tsx +@apps/web/src/app/(auth)/reset-password/page.tsx +@apps/web/src/components/settings/account-settings-form.tsx +@apps/web/src/messages/de.json + + + + + + Task 1: Markenbausteine — Icon-Dateien, Farbkonstanten, TesseraLogo-Komponente, i18n-Schluessel + apps/web/src/app/icon.svg, apps/web/src/app/apple-icon.png, apps/web/src/components/brand/brand.ts, apps/web/src/components/brand/tessera-logo.tsx, apps/web/src/components/brand/tessera-logo.test.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json + + - Variante `mark`: es entsteht ein Element mit der Rolle `img`, dessen zugaenglicher Name aus dem Uebersetzungsschluessel `common.brand.logoAlt` stammt. Ein sichtbarer Schriftzug ist NICHT vorhanden. + - Variante `horizontal`: Rolle `img` vorhanden UND der Schriftzug aus `common.appName` ist als Text sichtbar. + - Variante `stacked`: Rolle `img` vorhanden UND der Schriftzug ist sichtbar. + - Die Platte traegt bei Vorgabewerten eine Klasse, die die Kontur nur im dunklen Erscheinungsbild einschaltet; mit `plateOutline={false}` fehlt diese Klasse. + - Die Grundflaeche der Bildmarke enthaelt fuenf Kacheln, davon genau eine mit dem Gelbwert der Marke und genau eine mit einer Drehung. + - Sowohl `de.json` als auch `en.json` definieren `common.brand.logoAlt` — der Test liest beide Dateien und vergleicht die Schluesselmenge unterhalb von `common.brand`, damit keine Sprache spaeter allein ergaenzt werden kann. + + +Kopiere `user-files/logo/favicon.svg` unveraendert nach `apps/web/src/app/icon.svg` und +`user-files/logo/apple-touch-icon.png` unveraendert nach +`apps/web/src/app/apple-icon.png`. Nur diese beiden Dateien — die uebrigen gelieferten +Dateien bleiben ausserhalb des Repositories, `tessera-logo.svg` ist nachweislich +byteweise identisch zu `favicon.svg` und wuerde eine zweite Kopie desselben Inhalts +erzeugen. Beide Zieldateien MUESSEN unter `src/app/` liegen, damit Next.js sie als +dateibasierte Metadaten erkennt; `layout.tsx` wird nicht angefasst. + +Lege `apps/web/src/components/brand/brand.ts` an. Die Datei exportiert drei benannte +Konstanten fuer die drei Farben der Marke: den Gelbwert der gedrehten Signalkachel als +`BRAND_YELLOW`, den Olivton der vier uebrigen Kacheln als `BRAND_OLIVE` und die Farbe +der abgerundeten Grundplatte als `BRAND_PLATE`. Die Werte werden NICHT aus dieser +Beschreibung abgeschrieben, sondern beim Umsetzen direkt aus +`user-files/logo/favicon.svg` uebernommen — dort steht die Plattenfarbe am `rect` mit +`rx="14"`, der Olivton an den vier achsenparallelen Kacheln und der Gelbwert an der +Kachel mit `transform="rotate(...)"`. Ein kurzer Dateikommentar haelt fest, dass dies +die einzige Stelle im Web-Quellcode ist, an der eine Markenfarbe notiert wird, und dass +eine spaetere mandantenspezifische Gestaltung hier ansetzt. Schreibe in diesen Kommentar +keinen weiteren Farbwert hinein — die Konstanten tragen die Werte, der Kommentar +beschreibt nur ihren Zweck. + +Lege `apps/web/src/components/brand/tessera-logo.tsx` an, als `'use client'`-Komponente, +weil sie `useTranslations` aus `next-intl` benutzt. Exportiere `TesseraLogo` mit den +Eigenschaften `variant` (`'mark' | 'horizontal' | 'stacked'`, Vorgabe `'horizontal'`), +`size` (Kantenlaenge der Bildmarke in Pixeln, Vorgabe 32), `plateOutline` (Vorgabe +`true`), `className` und `wordmarkClassName`. + +Die Bildmarke wird als JSX-SVG von Hand gesetzt: `viewBox="0 0 72 72"`, `width`/`height` +aus `size`, `role="img"` und `aria-label` aus `t('brand.logoAlt')` bei +`useTranslations('common')`. Geometrie und Reihenfolge der sechs Rechtecke werden 1:1 aus +`user-files/logo/favicon.svg` uebernommen — Position, Groesse, Eckenradius und die +Drehung der Signalkachel unveraendert. Die Fuellfarben kommen aus den drei Konstanten, +nicht als Zahlenwerte im JSX. Setze das SVG als echte JSX-Elemente zusammen; verwende +ausdruecklich NICHT `dangerouslySetInnerHTML` und lies die Datei auch nicht zur Laufzeit +ein. + +Die Grundplatte erhaelt zusaetzlich `strokeWidth={2}` und, sofern `plateOutline` wahr ist, +eine Klassenkombination, die die Kontur im hellen Erscheinungsbild durchsichtig laesst und +im dunklen auf eine halbtransparente weisse Linie schaltet (Tailwind-Utilities +`stroke-transparent` und die zugehoerige `dark:`-Variante, etwa mit rund 25 Prozent +Deckung). Ist `plateOutline` falsch, bleibt es bei der durchsichtigen Kontur ohne +`dark:`-Variante. So bleibt die Umschaltung reines CSS — kein Theme-Hook, kein Flackern +beim Seitenaufbau. + +Die Variante `mark` liefert nur das SVG. `horizontal` liefert einen `inline-flex`-Container +mit Bildmarke und daneben dem Schriftzug aus `t('appName')`, gesetzt in halbfett, +leicht negativer Laufweite und in der Vordergrundfarbe der Oberflaeche (nicht in der +Primaerfarbe); `wordmarkClassName` wird an das Schriftzug-Element angehaengt, damit +aufrufende Stellen die Farbe ueberschreiben koennen. `stacked` liefert dieselben beiden +Bausteine als zentrierte Spalte, Bildmarke oben, Schriftzug darunter deutlich groesser. +`inline-flex` ist Absicht: die aufrufenden Seiten zentrieren ueber `text-center` des +Elternelements, ein Block-Element wuerde dort links kleben. + +Ergaenze in `de.json` und `en.json` unterhalb von `common` das Objekt `brand` mit dem +Schluessel `logoAlt` ("Tessera Logo" beziehungsweise "Tessera logo"). Beide Dateien +bekommen exakt denselben Schluesselbaum. + +Schreibe `apps/web/src/components/brand/tessera-logo.test.tsx` nach dem Muster von +`sidebar.test.tsx`: `useTranslations` aus `next-intl` per `vi.mock` ersetzen, Rueckgabe +ueber eine flache Zuordnung mit `appName` und `'brand.logoAlt'`. Decke die unter +`behavior` genannten Faelle ab und lies fuer die Sprachparitaet `de.json` und `en.json` +direkt als Modul ein, so wie es `src/messages/tenderRadar-parity.spec.ts` vormacht. + + + cmp user-files/logo/favicon.svg apps/web/src/app/icon.svg && cmp user-files/logo/apple-touch-icon.png apps/web/src/app/apple-icon.png && pnpm --filter @tessera/web exec vitest run src/components/brand && pnpm --filter @tessera/web type-check + + Die beiden Icon-Dateien liegen byteidentisch zur Lieferung unter `src/app/`. `TesseraLogo` rendert in allen drei Varianten, der Test ist gruen, beide Sprachdateien tragen den neuen Schluessel, die Typpruefung ist sauber. + + + + Task 2: Alle sechs Marken-Stellen auf die Komponente umstellen und den Gelbwert zusammenfuehren + apps/web/src/components/layout/header.tsx, apps/web/src/components/layout/sidebar.tsx, apps/web/src/components/layout/sidebar.test.tsx, apps/web/src/app/(auth)/login/page.tsx, apps/web/src/app/(auth)/reset-password/page.tsx, apps/web/src/app/(auth)/reset-password/[token]/page.tsx, apps/web/src/components/settings/account-settings-form.tsx + + - `sidebar.test.tsx` bleibt gruen: die Uebersetzungs-Attrappe kennt zusaetzlich den Schluessel `brand.logoAlt` im Bereich `common`, sodass die Bildmarke in der mobilen Schublade einen echten zugaenglichen Namen traegt statt des Schluesselnamens. + - Die vorhandenen Aussagen von `sidebar.test.tsx` werden nicht abgeschwaecht und keine geloescht. + + +Ersetze in `header.tsx` das Schriftzug-Element neben der Menue-Schaltflaeche durch +``. Der umgebende Flex-Container mit +`gap-3` bleibt unveraendert. + +Ersetze in `sidebar.tsx` das Schriftzug-Element in der Kopfzeile der mobilen Schublade +durch ``. Die Schliessen-Schaltflaeche +rechts daneben bleibt unveraendert. In `sidebar.test.tsx` ergaenze die +Uebersetzungs-Attrappe im Bereich `common` um den Eintrag `'brand.logoAlt'` mit einem +sprechenden Wert; die Attrappe arbeitet bereits mit punktierten Schluesseln. + +Baue in `login/page.tsx` die linke, gelbe Spalte um. Der Gelbwert steht dort heute als +Hintergrund-Utility in eckigen Klammern und ein zweites Mal im Kopfkommentar der Datei. +Beide Vorkommen verschwinden: das Utility wird durch ein Inline-`style` mit +`backgroundColor: BRAND_YELLOW` ersetzt (Import aus `@/components/brand/brand`), alle +uebrigen Klassen der Spalte bleiben stehen; der Kopfkommentar wird ohne Nennung eines +Farbwerts neu formuliert und verweist stattdessen auf die Konstante. Setze auf derselben +Spalte zusaetzlich `className="text-gray-900"` an den inneren, zentrierten Container, +damit der Schriftzug die dunkle Schrift des Panels erbt. + +Innerhalb der gelben Spalte entfaellt die Schriftzug-Ueberschrift ersatzlos, und ebenso +ersatzlos entfaellt der handgezeichnete Platzhalter — das ist der ``-Block mit den +vier gleich grossen Rechtecken im 24er-Koordinatensystem samt seinem `mt-4`-Wrapper. An +ihre Stelle tritt, als erstes Element der Spalte, ``. `plateOutline={false}` ist hier Pflicht: das Panel +ist unabhaengig vom Erscheinungsbild immer gelb, eine helle Kontur waere dort falsch. +Der bestehende Absatz mit `t('branding.tagline')` bleibt unveraendert darunter stehen. + +In der nur auf schmalen Fenstern sichtbaren Marken-Gruppe derselben Datei entfaellt die +Schriftzug-Ueberschrift ebenfalls ersatzlos; an ihre Stelle tritt ``. Der Container traegt bereits `text-center`, die +Komponente zentriert sich dadurch von selbst. Der Absatz mit dem Werbesatz bleibt. + +Ersetze in `reset-password/page.tsx` und in `reset-password/[token]/page.tsx` jeweils die +Schriftzug-Ueberschrift ueber der Seitenueberschrift durch ``. Die jeweils folgende `

` mit dem Seitentitel +bleibt unveraendert — sie ist die eigentliche Ueberschrift der Seite und darf nicht +mitentfernt werden. + +Ersetze in `account-settings-form.tsx` den Rumpf der lokalen Konstante `DEFAULT_ACCENT` +durch den aus `@/components/brand/brand` importierten `BRAND_YELLOW`. Der Name +`DEFAULT_ACCENT` bleibt bestehen, damit die drei bestehenden Verwendungsstellen +unveraendert bleiben; nur der Wert kommt jetzt aus der Markendatei. + + + set -e; test "$(grep -rl 'ffed00' apps/web/src --include='*.ts' --include='*.tsx')" = 'apps/web/src/components/brand/brand.ts'; test "$(grep -rl 'font-extrabold' 'apps/web/src/app/(auth)' --include='*.tsx' | wc -l)" -eq 0; test "$(grep -c 'width="7" height="7"' 'apps/web/src/app/(auth)/login/page.tsx' || true)" -eq 0; for f in apps/web/src/components/layout/header.tsx apps/web/src/components/layout/sidebar.tsx 'apps/web/src/app/(auth)/login/page.tsx' 'apps/web/src/app/(auth)/reset-password/page.tsx' 'apps/web/src/app/(auth)/reset-password/[token]/page.tsx'; do grep -q 'TesseraLogo' "$f"; done; pnpm --filter @tessera/web exec vitest run; pnpm --filter @tessera/web type-check; docker compose build web && docker compose up -d --force-recreate web; for i in $(seq 1 30); do curl -sf -o /dev/null http://localhost:3000/login && break || sleep 2; done; curl -sf -o /dev/null http://localhost:3000/login + + Der Gelbwert der Marke steht im gesamten Web-Quellcode nur noch in `brand.ts`. In den Anmelde- und Passwort-Seiten steht kein von Hand gesetzter Schriftzug mehr und kein Platzhalter-Icon. Alle fuenf Verwendungsstellen ziehen `TesseraLogo`. Die vollstaendige Web-Testsuite und die Typpruefung sind gruen. Das LOKALE Web-Abbild ist neu gebaut, der Container laeuft mit dem neuen Stand und die Anmeldeseite antwortet — die Abnahme in Task 3 sieht damit garantiert den neuen Code. Der Testserver alpha.tessera.ctl.de wird dabei NICHT angefasst. + + + + Task 3: Abnahme im Browser — beide Erscheinungsbilder, sieben Stellen + +Das gelieferte Tessera-Logo ist an sechs Stellen der Oberflaeche fest eingebaut und die +Icon-Dateien fuer den Browser-Tab liegen im Repository. Alle Stellen ziehen aus einer +einzigen Komponente `apps/web/src/components/brand/tessera-logo.tsx`; die Markenfarben +stehen ausschliesslich in `apps/web/src/components/brand/brand.ts`. Der handgezeichnete +Vier-Quadrate-Platzhalter auf der Anmeldeseite ist entfernt. Der Schriftzug wird als +echter Text in der Hausschrift gesetzt, nicht als Bilddatei, damit er in beiden +Erscheinungsbildern lesbar bleibt. Task 2 hat das lokale Web-Abbild bereits neu gebaut +und den Container ersetzt — unter `http://localhost:3000` laeuft der neue Stand. Der +Testserver alpha.tessera.ctl.de wurde nicht angefasst. + + +`http://localhost:3000` oeffnen. Anmeldung mit `admin` / `admin123`; fuer die +Nutzersicht stehen `nutzer1` / `Test1234!` und `nutzer2` / `Test1234!` bereit. Das +Erscheinungsbild wird ueber den Umschalter rechts in der Kopfleiste gewechselt. + +Sollte die Oberflaeche wider Erwarten noch den alten Stand zeigen, hilft +`docker compose build web && docker compose up -d --force-recreate web` — ein blosses +`up` baut nicht neu. + +Jeder der folgenden sieben Punkte ist EINMAL im hellen und EINMAL im dunklen +Erscheinungsbild anzusehen: + +1. **Browser-Tab** — im Tab steht die dunkle abgerundete Kachel-Marke, nicht mehr das + leere Standard-Blatt. Bei hartnaeckigem alten Symbol den Tab neu laden mit geleertem + Zwischenspeicher. +2. **Kopfleiste** — Bildmarke links neben dem Schriftzug "Tessera", in einer Zeile, + senkrecht mittig zur Zeilenhoehe, nicht abgeschnitten. +3. **Seitenleisten-Schublade** — Browserfenster schmal ziehen, bis das Menue-Symbol + erscheint, Schublade oeffnen: dieselbe Marke in der Kopfzeile der Schublade, die + Schliessen-Schaltflaeche daneben unveraendert erreichbar. +4. **Anmeldeseite, breite Ansicht** — abmelden, `http://localhost:3000/login`, Fenster + breit: das gelbe Panel links zeigt oben die Bildmarke, darunter "Tessera", darunter + den bekannten Werbesatz. Das handgezeichnete Vier-Quadrate-Symbol ist verschwunden. + Das Gelb des Panels ist unveraendert dasselbe wie vorher. +5. **Anmeldeseite, schmale Ansicht** — Fenster schmal ziehen: das gelbe Panel entfaellt + wie bisher, ueber dem Anmeldeformular steht die Marke einzeilig und mittig. +6. **Passwort-vergessen-Seite** — `http://localhost:3000/reset-password`: Marke oben, + darunter unveraendert die Seitenueberschrift und das E-Mail-Feld. +7. **Passwort-neu-setzen-Seite** — `http://localhost:3000/reset-password/test123` + (irgendein Zeichenkettenwert genuegt, die Seite rendert vor der Pruefung): Marke oben, + darunter unveraendert Ueberschrift und beide Passwortfelder. + +Beim Durchgang im DUNKLEN Erscheinungsbild zusaetzlich pruefen: die dunkle Platte der +Bildmarke hebt sich in Kopfleiste, Schublade und auf den Passwort-Seiten durch eine feine +helle Kontur vom Hintergrund ab und verschwimmt nicht mit ihm. Auf dem gelben Anmelde- +Panel darf diese Kontur NICHT zu sehen sein. + +**Zwei Aenderungen ausdruecklich zur Freigabe oder Zurueckweisung:** + +- Der Schriftzug "Tessera" ist in Kopfleiste und Schublade jetzt dunkel (beziehungsweise + hell im dunklen Erscheinungsbild) statt wie bisher gelb. Passt das, oder soll das + bisherige Gelb zurueck? +- Auf dem gelben Anmelde-Panel steht die Bildmarke jetzt oben statt unten. Passt diese + Reihenfolge, oder soll die Marke wieder unter den Text? + + +"passt" — wenn alle sieben Punkte in beiden Erscheinungsbildern stimmen und beide +Gestaltungsfragen freigegeben sind. + +Andernfalls die abweichende Stelle benennen (welcher Punkt, welches Erscheinungsbild, +was ist zu sehen) beziehungsweise die gewuenschte Antwort auf die zwei Gestaltungsfragen +geben. Beides wird noch in diesem Plan nachgezogen und nicht vertagt. + + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| Lieferant -> Repository | Die Logo-Dateien stammen von aussen und werden ins Repository uebernommen | +| Browser -> ausgelieferte Icon-Route | Next.js liefert `icon.svg` unter einer oeffentlichen Route aus, ohne Anmeldung | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-FGV-01 | Tampering | tessera-logo.tsx | medium | mitigate | Eine fremde SVG-Datei als Zeichenkette in die Seite zu geben waere ein Einfallstor fuer eingebettete Skripte. Task 1 verbietet `dangerouslySetInnerHTML` und jedes Einlesen der Datei zur Laufzeit ausdruecklich: die Bildmarke wird als JSX von Hand gesetzt, React entwertet dabei jeden Inhalt. | +| T-FGV-02 | Information Disclosure | apps/web/src/app/icon.svg | low | accept | Das Icon wird ohne Anmeldung ausgeliefert. Es ist eine Bildmarke ohne jeden Bezug zu Mandanten oder Nutzern — genau das ist der Zweck eines Favicons. Bewusst getragen. | +| T-FGV-03 | Tampering | user-files/logo/ | low | mitigate | Die Lieferung liegt in einem vom Repository ausgeschlossenen Ordner und koennte unbemerkt ausgetauscht werden. Der Verify-Schritt von Task 1 vergleicht die kopierten Dateien byteweise gegen die Lieferung, sodass Kopie und Quelle nachweislich uebereinstimmen; ab dem Commit ist die Repository-Fassung die massgebliche. | +| T-FGV-SC | Tampering | npm/pip/cargo installs | n/a | n/a | Dieser Plan installiert kein Paket — keine neue Abhaengigkeit, kein Legitimitaets-Tor noetig. | + + + +- `pnpm --filter @tessera/web exec vitest run` ist vollstaendig gruen (der bestehende + Bestand plus der neue Komponententest). +- `pnpm --filter @tessera/web type-check` ist sauber. +- Der Gelbwert der Marke kommt im Web-Quellcode genau einmal vor, in `brand.ts`. +- `apps/web/src/app/icon.svg` und `apps/web/src/app/apple-icon.png` sind byteidentisch zu + den gelieferten Dateien. +- In `apps/web/src/app/(auth)/` steht kein von Hand gesetzter Marken-Schriftzug mehr. +- Die Abnahme aus Task 3 ist in beiden Erscheinungsbildern durchlaufen. + + + +- Browser-Tab, Kopfleiste, Seitenleisten-Schublade, Anmeldeseite in breiter und schmaler + Ansicht sowie beide Passwort-Seiten zeigen das gelieferte Logo. +- Schriftzug und Bildmarke bleiben in hellem UND dunklem Erscheinungsbild lesbar; die + dunkle Platte verschwimmt im dunklen Erscheinungsbild nicht mit dem Hintergrund. +- Es existiert genau eine Datei, die eine spaetere mandantenspezifische Gestaltung + anfassen muss: `apps/web/src/components/brand/` mit der Farbkonstanten-Datei und der + Logo-Komponente. +- Von der Lieferung wurde nichts doppelt uebernommen; die nicht uebernommenen Dateien + sind mit Grund benannt. +- Es wurde keinerlei Hochlade-, Einstellungs- oder Mandanten-Funktion fuer das Logo + gebaut. + + + +Create `.planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-SUMMARY.md` when done. + +Die SUMMARY muss festhalten: +- welche gelieferten Dateien uebernommen wurden und welche mit welcher Begruendung nicht, +- warum der Schriftzug als Text und nicht als Bilddatei ausgeliefert wird (Farbe fest + eingebacken UND Schriftart Archivo nicht geladen), +- wie die Kontur der Platte im dunklen Erscheinungsbild geloest ist und warum sie auf dem + gelben Anmelde-Panel abgeschaltet ist, +- dass der Gelbwert jetzt an genau einer Stelle steht und `--primary` bewusst unberuehrt + blieb, +- wie der Benutzer die beiden zur Freigabe gestellten Gestaltungsfragen entschieden hat. + +Ausserdem als Backlog-Punkt zu vermerken (vom Orchestrator einzureichen, in diesem Plan +NICHT zu bauen): "Administrator kann das Erscheinungsbild je Mandant anpassen — eigenes +Logo hochladen, Markenfarben setzen. Ansatzpunkt ist +`apps/web/src/components/brand/`; die Oberflaeche zieht das Logo bereits ausschliesslich +von dort." +