67 lines
2.5 KiB
TypeScript
67 lines
2.5 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { Breadcrumbs, type Crumb } from "@/components/Breadcrumbs";
|
|
import { OrganicBlob } from "@/components/OrganicBlob";
|
|
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo/jsonld";
|
|
import { getSiteUrl } from "@/lib/seo/config";
|
|
|
|
type PageHeaderProps = {
|
|
eyebrow?: string;
|
|
title: string;
|
|
lead?: ReactNode;
|
|
crumbs?: Crumb[];
|
|
/** Varies the header's decorative blob so every interior page doesn't open with the exact same rose-top-right treatment. */
|
|
blobTone?: "rose" | "sage" | "cream" | "mauve";
|
|
blobPosition?: "top-right" | "top-left";
|
|
/** Shorter vertical padding for pages that don't need much ceremony (e.g. Kontakt). */
|
|
compact?: boolean;
|
|
};
|
|
|
|
/** Renders the visual breadcrumb trail AND its matching BreadcrumbList JSON-LD from the same `crumbs` - every page that already passes `crumbs` gets structured breadcrumb data for free. */
|
|
export async function PageHeader({
|
|
eyebrow,
|
|
title,
|
|
lead,
|
|
crumbs,
|
|
blobTone = "rose",
|
|
blobPosition = "top-right",
|
|
compact = false,
|
|
}: PageHeaderProps) {
|
|
const breadcrumbData = crumbs?.length
|
|
? await (async () => {
|
|
const siteUrl = await getSiteUrl();
|
|
const items = [
|
|
{ name: "Startseite", url: siteUrl },
|
|
...crumbs.map((crumb) => ({ name: crumb.title, url: crumb.href ? `${siteUrl}${crumb.href}` : undefined })),
|
|
];
|
|
return breadcrumbJsonLd(items);
|
|
})()
|
|
: null;
|
|
|
|
const blobPositionClass = blobPosition === "top-left" ? "-left-20 -top-20" : "-right-20 -top-20";
|
|
|
|
return (
|
|
<section
|
|
className={`relative overflow-hidden border-b border-anouma-taupe/10 bg-anouma-cream-light ${
|
|
compact ? "pb-10 pt-10 sm:pb-12 sm:pt-12" : "pb-16 pt-14 sm:pb-20 sm:pt-20"
|
|
}`}
|
|
>
|
|
<OrganicBlob tone={blobTone} className={`${blobPositionClass} h-72 w-72`} />
|
|
<div className="relative mx-auto max-w-6xl px-6 sm:px-8 lg:px-12">
|
|
{breadcrumbData && <JsonLd data={breadcrumbData} />}
|
|
{crumbs && <Breadcrumbs items={crumbs} />}
|
|
{eyebrow && (
|
|
<p className="mb-4 text-xs font-medium uppercase tracking-[0.24em] text-anouma-plum">
|
|
{eyebrow}
|
|
</p>
|
|
)}
|
|
<h1 className="max-w-3xl text-balance font-serif text-4xl font-medium leading-tight text-anouma-plum sm:text-5xl lg:text-6xl">
|
|
{title}
|
|
</h1>
|
|
{lead && (
|
|
<div className="mt-5 max-w-2xl text-lg leading-relaxed text-anouma-plum">{lead}</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|