docs(quick-260907-fgv): Plan fuer festen Einbau von Tessera-Logo und Favicon
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
This commit is contained in:
+449
@@ -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"
|
||||
---
|
||||
|
||||
<objective>
|
||||
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 `<input type="color">`, 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 `<link rel="icon">`-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.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.claude/gsd-core/workflows/execute-plan.md
|
||||
@~/.claude/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@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
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="tracer" tdd="true">
|
||||
<name>Task 1: Markenbausteine — Icon-Dateien, Farbkonstanten, TesseraLogo-Komponente, i18n-Schluessel</name>
|
||||
<files>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</files>
|
||||
<behavior>
|
||||
- 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.
|
||||
</behavior>
|
||||
<action>
|
||||
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.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>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</automated>
|
||||
</verify>
|
||||
<done>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.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Alle sechs Marken-Stellen auf die Komponente umstellen und den Gelbwert zusammenfuehren</name>
|
||||
<files>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</files>
|
||||
<behavior>
|
||||
- `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.
|
||||
</behavior>
|
||||
<action>
|
||||
Ersetze in `header.tsx` das Schriftzug-Element neben der Menue-Schaltflaeche durch
|
||||
`<TesseraLogo variant="horizontal" size={28} />`. Der umgebende Flex-Container mit
|
||||
`gap-3` bleibt unveraendert.
|
||||
|
||||
Ersetze in `sidebar.tsx` das Schriftzug-Element in der Kopfzeile der mobilen Schublade
|
||||
durch `<TesseraLogo variant="horizontal" size={28} />`. 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 `<svg>`-Block mit den
|
||||
vier gleich grossen Rechtecken im 24er-Koordinatensystem samt seinem `mt-4`-Wrapper. An
|
||||
ihre Stelle tritt, als erstes Element der Spalte, `<TesseraLogo variant="stacked"
|
||||
size={112} plateOutline={false} />`. `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 `<TesseraLogo
|
||||
variant="horizontal" size={32} />`. 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 `<TesseraLogo
|
||||
variant="horizontal" size={32} />`. Die jeweils folgende `<h2>` 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.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>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</automated>
|
||||
</verify>
|
||||
<done>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.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking-human">
|
||||
<name>Task 3: Abnahme im Browser — beide Erscheinungsbilder, sieben Stellen</name>
|
||||
<what-built>
|
||||
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.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
`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?
|
||||
</how-to-verify>
|
||||
<resume-signal>
|
||||
"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.
|
||||
</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## 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. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- `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.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- 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.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
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."
|
||||
</output>
|
||||
Reference in New Issue
Block a user