feat(quick-260907-fgv): Anmeldeseite zeigt Logo links, Text rechts daneben

Auf Wunsch des Users stehen Bildmarke und Text auf dem gelben Anmelde-Panel jetzt
nebeneinander statt untereinander — dieselbe Anordnung wie in der gelieferten Datei
tessera-logo-schriftzug.svg: links die Marke, rechts der Name und darunter der
Untertitel.

Dafuer nimmt die horizontale Variante der Marken-Komponente einen optionalen
Untertitel entgegen. Die gestapelte Variante entfaellt — sie war nach dieser
Umstellung nirgends mehr im Einsatz, alle uebrigen fuenf Stellen standen schon vorher
nebeneinander.

Der Test auf die entfallene Variante wurde nicht geloescht, sondern durch zwei Tests
auf das neue Verhalten ersetzt: einer prueft, dass ohne Angabe kein Untertitel
erscheint, der andere die tatsaechliche Reihenfolge im Dokument (Marke vor Name, Name
vor Untertitel) statt nur deren Vorhandensein. Die Erwartungstexte sind von Hand
geschrieben und nicht aus derselben Quelle gebildet, aus der die Komponente sie zieht.

222/222 Web-Tests gruen, Typpruefung sauber, im Browser gegengeprueft.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
This commit is contained in:
2026-09-07 11:41:07 +02:00
parent f8d7da073b
commit 11c0ec63cb
3 changed files with 64 additions and 29 deletions
+10 -5
View File
@@ -43,11 +43,16 @@ export default function LoginPage() {
className="hidden md:flex md:flex-1 items-center justify-center"
style={{ backgroundColor: BRAND_YELLOW }}
>
<div className="flex flex-col items-center gap-6 px-8 text-center text-gray-900">
<TesseraLogo variant="stacked" size={112} plateOutline={false} />
<p className="text-xl text-gray-800 max-w-md leading-relaxed">
{t('branding.tagline')}
</p>
<div className="px-8 text-gray-900">
<TesseraLogo
variant="horizontal"
size={104}
plateOutline={false}
tagline={t('branding.tagline')}
className="gap-6"
wordmarkClassName="text-5xl"
taglineClassName="text-lg text-gray-800 max-w-xs"
/>
</div>
</div>
@@ -46,11 +46,36 @@ describe('TesseraLogo', () => {
expect(screen.getByText('Tessera')).toBeInTheDocument();
});
it('variant "stacked" renders the img-role mark and the visible wordmark text', () => {
render(<TesseraLogo variant="stacked" />);
it('renders no tagline unless one is passed', () => {
render(<TesseraLogo variant="horizontal" />);
expect(screen.getByRole('img', { name: 'Tessera Logo' })).toBeInTheDocument();
expect(screen.getByText('Tessera')).toBeInTheDocument();
// Hand-geschriebener Erwartungstext, bewusst NICHT aus derselben Quelle
// gebildet, aus der die Komponente ihn zoege.
expect(
screen.queryByText('Modulare Workflow-Plattform fuer Ihr Unternehmen'),
).not.toBeInTheDocument();
});
it('places mark, wordmark and tagline in that document order when a tagline is passed', () => {
render(
<TesseraLogo
variant="horizontal"
tagline="Modulare Workflow-Plattform fuer Ihr Unternehmen"
/>,
);
const mark = screen.getByRole('img', { name: 'Tessera Logo' });
const wordmark = screen.getByText('Tessera');
const tagline = screen.getByText(
'Modulare Workflow-Plattform fuer Ihr Unternehmen',
);
expect(mark).toBeInTheDocument();
// Node.compareDocumentPosition: DOCUMENT_POSITION_FOLLOWING === 4.
// Prueft die tatsaechliche Reihenfolge im Baum — Bildmarke vor Schriftzug,
// Schriftzug vor Untertitel — statt nur deren Vorhandensein.
expect(mark.compareDocumentPosition(wordmark) & 4).toBe(4);
expect(wordmark.compareDocumentPosition(tagline) & 4).toBe(4);
});
it('enables the dark-mode plate outline class by default', () => {
+25 -20
View File
@@ -3,13 +3,19 @@
import { useTranslations } from 'next-intl';
import { BRAND_OLIVE, BRAND_PLATE, BRAND_YELLOW } from './brand';
export type TesseraLogoVariant = 'mark' | 'horizontal' | 'stacked';
export type TesseraLogoVariant = 'mark' | 'horizontal';
export interface TesseraLogoProps {
/** Welche Bausteine gerendert werden. Vorgabe: `'horizontal'`. */
variant?: TesseraLogoVariant;
/** Kantenlaenge der Bildmarke in Pixeln. Vorgabe: 32. */
size?: number;
/**
* Optionaler Untertitel unter dem Schriftzug, rechts neben der Bildmarke —
* die Anordnung der gelieferten Datei `tessera-logo-schriftzug.svg`. Ohne
* Angabe steht rechts nur der Name. Nur fuer `variant="horizontal"`.
*/
tagline?: string;
/**
* Schaltet die halbtransparente helle Kontur der Grundplatte im dunklen
* Erscheinungsbild ein. Auf Flaechen, die unabhaengig vom
@@ -19,8 +25,10 @@ export interface TesseraLogoProps {
plateOutline?: boolean;
/** Zusaetzliche Klassen fuer das aeusserste Element der gewaehlten Variante. */
className?: string;
/** Zusaetzliche Klassen fuer den Schriftzug (nur `horizontal`/`stacked`). */
/** Zusaetzliche Klassen fuer den Schriftzug (nur `horizontal`). */
wordmarkClassName?: string;
/** Zusaetzliche Klassen fuer den Untertitel (nur zusammen mit `tagline`). */
taglineClassName?: string;
}
function LogoMark({
@@ -86,9 +94,11 @@ function LogoMark({
export function TesseraLogo({
variant = 'horizontal',
size = 32,
tagline,
plateOutline = true,
className,
wordmarkClassName,
taglineClassName,
}: TesseraLogoProps) {
const t = useTranslations('common');
const ariaLabel = t('brand.logoAlt');
@@ -104,26 +114,21 @@ export function TesseraLogo({
);
}
if (variant === 'stacked') {
return (
<span className={`inline-flex flex-col items-center gap-2 ${className ?? ''}`}>
<LogoMark size={size} plateOutline={plateOutline} ariaLabel={ariaLabel} />
<span
className={`text-2xl font-bold tracking-tight ${wordmarkClassName ?? ''}`}
>
// Bildmarke links, Schriftzug rechts daneben. Traegt die Komponente einen
// Untertitel, steht er unter dem Namen — dieselbe Anordnung wie in der
// gelieferten Datei `tessera-logo-schriftzug.svg`.
return (
<span className={`inline-flex items-center gap-3 ${className ?? ''}`}>
<LogoMark size={size} plateOutline={plateOutline} ariaLabel={ariaLabel} />
<span className="inline-flex flex-col text-left">
<span className={`font-bold tracking-tight leading-none ${wordmarkClassName ?? ''}`}>
{t('appName')}
</span>
</span>
);
}
return (
<span className={`inline-flex items-center gap-2 ${className ?? ''}`}>
<LogoMark size={size} plateOutline={plateOutline} ariaLabel={ariaLabel} />
<span
className={`font-bold tracking-tight ${wordmarkClassName ?? ''}`}
>
{t('appName')}
{tagline ? (
<span className={`mt-1 leading-snug ${taglineClassName ?? ''}`}>
{tagline}
</span>
) : null}
</span>
</span>
);