Low Hum Studios, Room A
Take a slow look around Room A before you book it. Keep scrolling to walk through the gear that shapes every session.
A pinned scroll feature section that tours one large photograph like a camera move. It opens on the whole picture of a recording studio, then glides and zooms into five details in turn, holding at each one, and eases back out to the whole picture at the end. Each stop is set in the demo data as a point and a zoom, and the camera breathes out a little between close-ups while a soft vignette tightens as it moves in. Beside the photograph, the eyebrow and heading stay put while one detail at a time crossfades in with its title and two sentences, above a row of hairline segments that fill as the tour goes on. The tour closes with a short line, an Arrow Pill and an underline link. On phones the text sits in a sheet below the photograph. Reduced motion shows the whole photograph with the details as a plain list.
A pinned scroll section that tours one large photograph like a camera move: it opens on the whole picture, glides and zooms into each detail in turn, holds, and eases back out at the end. Beside the photograph the eyebrow and heading stay put while one detail at a time crossfades in above a row of hairline segments, and the tour closes with a short line, an Arrow Pill and an underline link.
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/feature321?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/feature321?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/feature321.tsx plus the button23 and button2 buttons it uses.
The installed file exports feature321Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Feature321, feature321Demo } from "@/components/beste/block/feature321";
export default function Page() {
return <Feature321 {...feature321Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Feature321 } from "@/components/beste/block/feature321";
export default function Page() {
return (
<Feature321
eyebrow="Low Hum Studios, Room A"
heading="Every part of the room is part of the sound."
description="Take a slow look around Room A before you book it."
image={{
src: "https://images.unsplash.com/photo-1598488035139-bdbb2231ce04?w=2400&q=85",
alt: "A recording studio control room with guitars on the wall above an analog mixing console and racks of outboard gear",
}}
imageAspect={1.5}
details={[
{
title: "The guitar wall",
text: "Six guitars, set up and tuned the morning of your session.",
x: 0.39,
y: 0.18,
zoom: 1.9,
},
{
title: "The console",
text: "A 48-channel analog desk, restored by hand in 2019.",
x: 0.3,
y: 0.61,
zoom: 2.2,
},
]}
closing="Room A is open most Tuesdays and every other weekend."
buttons={[
{ label: "Book Room A", href: "/book" },
{ label: "See Room B", href: "/rooms/b" },
]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small muted line above the heading |
heading | string | – | Section heading, rendered as an h2 |
description | string | – | Shown beside the whole photograph before the tour starts |
image | { src: string; alt: string } | – | The photograph the camera tours |
imageAspect | number | 1.5 | Width divided by height of the photograph |
details | TourDetail[] | [] | The stops of the tour, in order |
closing | string | – | Shown beside the whole photograph once the tour ends |
buttons | ActionButton[] | [] | Shown with the closing line; the first renders as a dark Arrow Pill, every later one as a Button2 underline link |
className | string | – | Extra classes for the outer <section> |
type ActionButton = {
label: string;
href: string;
};
type TourDetail = {
title: string;
text: string;
x: number; // center across the photo, 0 (left) to 1 (right)
y: number; // center down the photo, 0 (top) to 1 (bottom)
zoom: number; // 1 shows the whole photo, clamped to 1 through 6
};
type Image = {
src: string;
alt: string;
};400svh tall with a sticky stage of 100svh - 78px (at least 640px). Scroll progress, smoothed by a spring, is split into a short hold on the whole photo, then a move and a hold for each detail, then a move back out.invisible and aria-hidden, and 1 hairline segment per detail fills as its stop is reached.imageAspect and re-measured by a ResizeObserver; the photo fades in on load.md the text takes 4 columns and the photo 8.h3 items, then the closing line and buttons.