A dark ink hero with drifting soft light and film grain: eyebrow, a serif headline settling in word by word, intro, a light sliding-marker pill and a ruled fact sheet on the left, beside a portrait photograph drifting inside its rounded frame.
An evening hero on the ink surface under film grain: the headline settles in word by word above the intro, a pale pill and a ruled fact table, with a portrait photograph drifting inside its frame alongside.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/hero178?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/hero178?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/hero178.tsx plus the button22 sliding-marker pill and the text1 word stagger it uses.
The installed file exports hero178Demo alongside the block: the exact props behind the preview above. Spread it to get a working hero in one line.
import { Hero178, hero178Demo } from "@/components/beste/block/hero178";
export default function Page() {
return <Hero178 {...hero178Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Hero178 } from "@/components/beste/block/hero178";
export default function Page() {
return (
<Hero178
eyebrow="Evening sessions now open"
heading="The day is loud. This hour is not."
description="Our evening practice runs from six until nine."
button={{ label: "Reserve an evening", href: "/evenings", tone: "light" }}
image={{ src: "/photos/dock.jpg", alt: "A wooden dock reaching into a still lake at dusk" }}
facts={[
{ label: "Hours", value: "18:00 to 21:00, Monday to Thursday" },
{ label: "Length", value: "60 or 75 minutes" },
]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small-caps line above the headline |
heading | string | – | Headline, rendered as the page h1 through Text1 |
description | string | – | Paragraph under the headline |
button | ActionButton | – | Pill action, defaulting to the light tone on this dark surface |
image | { src: string; alt: string } | – | Portrait photograph |
facts | Fact[] | [] | Ruled fact rows under the action |
className | string | – | Extra classes for the outer <section> |
type Fact = {
label: string;
value: string;
};
type ActionButton = {
label: string;
href: string;
tone?: "primary" | "light" | "dark" | "outline";
};button falls back to the light tone rather than primary, which is the correct default on a dark surface: an unspecified button comes out pale instead of taking the brand colour.bg-background/10 placeholder rather than bg-muted, because a light muted panel would flash bright against the ink surface while the file loads.dl on a three-column grid with the value spanning two, so a long value wraps under itself instead of pushing its label out of alignment; the rules are written against background/20 for the dark surface.useId, so two grain surfaces on one page cannot collide on a global SVG id.trigger="mount", since a hero is above the fold, and the rest of the column is offset from 0.4 + heading.split(" ").length * 0.07.