feat(quick-260907-fgv-01): Markenbausteine — Icons, Farbkonstanten, TesseraLogo-Komponente
- favicon.svg und apple-touch-icon.png byteidentisch nach src/app/ kopiert (dateibasierte Next.js-Metadaten fuer den Browser-Tab) - brand.ts: einzige Stelle im Web-Quellcode fuer die drei Markenfarben (BRAND_YELLOW, BRAND_OLIVE, BRAND_PLATE), aus favicon.svg uebernommen - TesseraLogo-Komponente mit den Varianten mark/horizontal/stacked, als JSX-SVG von Hand gesetzt (kein dangerouslySetInnerHTML), Kontur-Klasse fuer die dunkle Platte per plateOutline-Eigenschaft schaltbar - common.brand.logoAlt in de.json und en.json ergaenzt, Schluesselparitaet per Test abgesichert Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { BRAND_OLIVE, BRAND_PLATE, BRAND_YELLOW } from './brand';
|
||||
|
||||
export type TesseraLogoVariant = 'mark' | 'horizontal' | 'stacked';
|
||||
|
||||
export interface TesseraLogoProps {
|
||||
/** Welche Bausteine gerendert werden. Vorgabe: `'horizontal'`. */
|
||||
variant?: TesseraLogoVariant;
|
||||
/** Kantenlaenge der Bildmarke in Pixeln. Vorgabe: 32. */
|
||||
size?: number;
|
||||
/**
|
||||
* Schaltet die halbtransparente helle Kontur der Grundplatte im dunklen
|
||||
* Erscheinungsbild ein. Auf Flaechen, die unabhaengig vom
|
||||
* Erscheinungsbild immer hell sind (etwa das gelbe Anmelde-Panel), muss
|
||||
* dies auf `false` gesetzt werden. Vorgabe: `true`.
|
||||
*/
|
||||
plateOutline?: boolean;
|
||||
/** Zusaetzliche Klassen fuer das aeusserste Element der gewaehlten Variante. */
|
||||
className?: string;
|
||||
/** Zusaetzliche Klassen fuer den Schriftzug (nur `horizontal`/`stacked`). */
|
||||
wordmarkClassName?: string;
|
||||
}
|
||||
|
||||
function LogoMark({
|
||||
size,
|
||||
plateOutline,
|
||||
className,
|
||||
ariaLabel,
|
||||
}: {
|
||||
size: number;
|
||||
plateOutline: boolean;
|
||||
className?: string;
|
||||
ariaLabel: string;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 72 72"
|
||||
width={size}
|
||||
height={size}
|
||||
role="img"
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
>
|
||||
<rect
|
||||
x="4"
|
||||
y="4"
|
||||
width="64"
|
||||
height="64"
|
||||
rx="14"
|
||||
fill={BRAND_PLATE}
|
||||
strokeWidth={2}
|
||||
className={
|
||||
plateOutline
|
||||
? 'stroke-transparent dark:stroke-white/25'
|
||||
: 'stroke-transparent'
|
||||
}
|
||||
/>
|
||||
<g>
|
||||
<rect x="16" y="16" width="12" height="12" rx="2.5" fill={BRAND_OLIVE} />
|
||||
<rect x="30" y="16" width="12" height="12" rx="2.5" fill={BRAND_OLIVE} />
|
||||
<rect
|
||||
x="45"
|
||||
y="15"
|
||||
width="12"
|
||||
height="12"
|
||||
rx="2.5"
|
||||
transform="rotate(12 51 21)"
|
||||
fill={BRAND_YELLOW}
|
||||
/>
|
||||
<rect x="30" y="30" width="12" height="12" rx="2.5" fill={BRAND_OLIVE} />
|
||||
<rect x="30" y="44" width="12" height="12" rx="2.5" fill={BRAND_OLIVE} />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zentrale Bildmarken-Komponente von Tessera. Buendelt die Bildmarke, den
|
||||
* Schriftzug und deren Anordnung an einer einzigen Stelle -- eine spaetere
|
||||
* mandantenspezifische Gestaltung setzt hier an, nicht an den einzelnen
|
||||
* Verwendungsstellen.
|
||||
*/
|
||||
export function TesseraLogo({
|
||||
variant = 'horizontal',
|
||||
size = 32,
|
||||
plateOutline = true,
|
||||
className,
|
||||
wordmarkClassName,
|
||||
}: TesseraLogoProps) {
|
||||
const t = useTranslations('common');
|
||||
const ariaLabel = t('brand.logoAlt');
|
||||
|
||||
if (variant === 'mark') {
|
||||
return (
|
||||
<LogoMark
|
||||
size={size}
|
||||
plateOutline={plateOutline}
|
||||
className={className}
|
||||
ariaLabel={ariaLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 text-foreground ${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 text-foreground ${wordmarkClassName ?? ''}`}
|
||||
>
|
||||
{t('appName')}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user