UI Library
Centered hero
A centered landing hero with a status pill, a gradient wash, and two calls to action. The wash is pointer-events-none, so it never intercepts a click on the buttons beneath it.
Herolandinggradientcentered
Preview
A small studio building focused, private tools
Focused tools that respect your data.
No dark patterns, no data selling, open-source where it counts. Everything here is built to keep working — not to keep your attention.
apps/site/blocks/components/hero-centered.tsx
import { ArrowRight } from 'lucide-react';
interface HeroCenteredProps {
eyebrow?: string;
title?: string;
body?: string;
primaryCta?: { label: string; href: string };
secondaryCta?: { label: string; href: string };
}
export function HeroCentered({
eyebrow = 'A small studio building focused, private tools',
title = 'Focused tools that respect your data.',
body = 'No dark patterns, no data selling, open-source where it counts. Everything here is built to keep working — not to keep your attention.',
primaryCta = { label: 'Explore the tools', href: '#' },
secondaryCta = { label: 'Read our principles', href: '#' },
}: HeroCenteredProps) {
return (
<section className="relative overflow-hidden border-b border-border/60">
{/* Decorative wash. `pointer-events-none` so it never eats a click on the
CTAs beneath it, and aria-hidden so it is not announced. */}
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 -top-32 h-72 bg-gradient-to-b from-primary/10 via-primary/5 to-transparent blur-2xl"
/>
<div className="relative mx-auto max-w-3xl px-4 py-20 text-center sm:px-6 sm:py-28">
<div className="inline-flex items-center gap-2 rounded-full border border-border/80 bg-background px-3 py-1 text-xs text-muted-foreground">
<span className="h-1.5 w-1.5 rounded-full bg-primary" />
{eyebrow}
</div>
<h1 className="mt-6 text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl">
{title}
</h1>
<p className="mx-auto mt-6 max-w-2xl text-base text-muted-foreground sm:text-lg">
{body}
</p>
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
<a
href={primaryCta.href}
className="inline-flex h-10 items-center gap-2 rounded-md bg-primary px-5 text-sm font-medium text-primary-foreground shadow-sm transition-colors hover:bg-primary/90"
>
{primaryCta.label}
<ArrowRight className="h-4 w-4" />
</a>
<a
href={secondaryCta.href}
className="inline-flex h-10 items-center gap-2 rounded-md border border-border bg-background px-5 text-sm font-medium transition-colors hover:bg-accent"
>
{secondaryCta.label}
</a>
</div>
</div>
</section>
);
}
Dependencies
lucide-react— npm package
Colours come from CSS variables (--background, --primary, --border) using the shadcn/ui token names, so the block adopts your palette rather than importing ours.