A photobook on cotton paper
Twelve years of walking one coastline, collected in a clothbound book and printed on heavy cotton paper in an edition of 800.
A pinned scroll hero that starts as blank paper with an eyebrow and a large serif headline. As the page scrolls, a landscape photo blooms across the paper like watercolor from three points in a live WebGL shader, with darker pigment rims and a pale wash ahead of each edge, until it fills the screen and the headline, an intro and two links settle in over it. Scrolling back dries it to paper again. Paper follows the theme, it pauses offscreen, shows the finished frame for reduced motion and cross-fades a plain photo without WebGL.
A pinned scroll hero that opens as blank paper with a centered serif headline, then blooms a landscape photo across the paper from 3 points in a WebGL watercolor shader as the page scrolls, until the headline, an intro and two links settle in over the finished photo.
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/hero206?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/hero206?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/hero206.tsx.
The installed file exports hero206Demo alongside the block: the exact props behind the preview above. Spread it to get a working hero in one line.
import { Hero206, hero206Demo } from "@/components/beste/block/hero206";
export default function Page() {
return <Hero206 {...hero206Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Hero206 } from "@/components/beste/block/hero206";
export default function Page() {
return (
<Hero206
eyebrow="A photobook on cotton paper"
heading="Every place arrives slowly."
description="Twelve years of walking one coastline, collected in a clothbound book."
buttons={[
{ label: "Order the book", href: "https://beste.co" },
{ label: "Leaf through it", href: "/book" },
]}
image={{
src: "https://images.unsplash.com/photo-1701870856373-bc7273fe6c2c?w=2000&q=80",
alt: "A rocky cove with wildflowers above turquoise water",
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small line above the opening headline |
heading | string | – | Serif headline, shown centered on paper and again over the finished photo |
description | string | – | Intro paragraph in the closing overlay |
buttons | ActionButton[] | [] | The first renders as an arrow pill in the theme's primary color, the rest as underlined white links |
image | { src: string; alt: string } | – | Photo painted in by the shader; alt is exposed through an sr-only span |
className | string | – | Extra classes for the outer <section> |
type ActionButton = {
label: string;
href: string;
};260vh tall with a sticky stage of 100svh minus 78px (at least 640px). The bloom maps page scroll from 5% to 38% of the section onto 0 to 1; the canvas eases toward it, filling slowly and drying back faster when you scroll up.bg-background and re-read when the class, style or data-theme of any ancestor changes, so it follows the theme, even one scoped to a wrapper.<img> cross-fades in between 8% and 35% scroll instead. The image must allow cross-origin loading for the shader.h1; the copy in the closing overlay is an aria-hidden paragraph. With reduced motion the section is 1 screen tall, the photo is fully painted and the closing overlay is shown with a real h1.