Lisbon
Príncipe Real
Rua da Escola Politécnica 58 1250-102 Lisbon
Open every day from 7:30 am to 6 pm.
Get directionsFerro Coffee House, five rooms in five cities
The same beans, the same slow bar and the same long tables, from a sunny corner in Lisbon to a lamp-lit room in Melbourne.
Príncipe Real
Rua da Escola Politécnica 58 1250-102 Lisbon
Open every day from 7:30 am to 6 pm.
Get directionsVesterbro
Istedgade 88 1650 Copenhagen
Weekdays from 7 am, weekends from 8 am, until 5 pm.
Get directionsWilliamsburg
212 Wythe Avenue Brooklyn, NY 11249
Open every day from 7 am to 7 pm, later on Fridays.
Get directionsFitzroy
301 Smith Street Fitzroy VIC 3065
Weekdays from 7 am to 4 pm, weekends until 5 pm.
Get directionsNext, Mexico City, in the spring.
A pinned scroll feature section for a coffee house with rooms in several cities, built on the scroll-rail component: the heading, intro, an Arrow Pill and an underline link open a travel guide that moves sideways as the page scrolls. Each location is a tall photo card with the picture moving slower than its frame, and a soft shade at the foot holding the city name, the neighborhood, a two-line address, the opening hours and a Get directions link. The city names also run as a giant faint row behind the cards, drifting at their own slower pace for depth, and a quiet closing line ends the rail. Reduced motion turns the rail into a plain horizontal scroll.
A pinned scroll section where an intro panel and one tall photo card per location travel sideways on a ScrollRail as the page scrolls, each photo drifting slower than its card, while the city names drift as a giant faint row behind them.
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/feature319?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/feature319?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/feature319.tsx plus the scroll-rail component and the button23 and button2 buttons it uses.
The installed file exports feature319Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Feature319, feature319Demo } from "@/components/beste/block/feature319";
export default function Page() {
return <Feature319 {...feature319Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Feature319 } from "@/components/beste/block/feature319";
export default function Page() {
return (
<Feature319
eyebrow="Ferro Coffee House, five rooms in five cities"
heading="Pull up a chair in any of them."
description="The same beans, the same slow bar and the same long tables."
buttons={[
{ label: "Find the nearest room", href: "/locations" },
{ label: "Our coffee", href: "https://beste.co" },
]}
places={[
{
city: "Lisbon",
neighborhood: "Príncipe Real",
address: "Rua da Escola Politécnica 58\n1250-102 Lisbon",
hours: "Open every day from 7:30 am to 6 pm.",
image: {
src: "https://images.unsplash.com/photo-1753236404576-b4c1a4b5f0dc?w=1400&q=80",
alt: "A sunlit street in Lisbon",
},
directions: { label: "Get directions", href: "https://beste.co" },
},
{
city: "Brooklyn",
neighborhood: "Williamsburg",
address: "212 Wythe Avenue\nBrooklyn, NY 11249",
image: {
src: "https://images.unsplash.com/photo-1559339352-11d035aa65de?w=1400&q=80",
alt: "A warm dining room with long tables in Brooklyn",
},
},
]}
closing="Next, Mexico City, in the spring."
rail={{ gap: 40, smoothing: 0.5 }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Small line above the heading on the intro panel |
heading | string | – | Section heading, rendered as an h2 |
description | string | – | Intro paragraph under the heading |
buttons | ActionButton[] | [] | The first renders as a primary arrow pill (Button23), the rest as muted underline links (Button2) |
places | Place[] | [] | One tall card per location, in travel order |
closing | string | – | Quiet last line at the end of the rail |
backdrop | boolean | true | Shows the city names as a giant faint row drifting behind the cards |
rail | Omit<ScrollRailProps, "children" | "progress" | "autoplay" | "align" | "className"> | – | Forwards ScrollRail props (gap, smoothing, speed, paused); progress, autoplay and alignment are set by the block |
className | string | – | Extra classes for the outer <section> |
type ActionButton = {
label: string;
href: string;
};
type Place = {
city: string;
neighborhood?: string;
address?: string; // "\n" breaks it onto two lines
hours?: string;
image: { src: string; alt: string };
directions?: ActionButton;
};320svh tall with a sticky stage of 100svh minus 78px (at least 640px). Rail progress maps page scroll from 2% to 96% of the section onto 0 to 1, clamped.0% to -46% at its own pace, so it reads as a layer far behind the cards. It is aria-hidden and only renders when backdrop is on and there is at least 1 place.data-rail-depth="0.2", so it drifts inside the card. The caption block uses data-rail-focus="0.55" and the closing line 0.35, so both dim away from the middle of the viewport.h3. The address keeps its line breaks through whitespace-pre-line, and a dark gradient at the card foot keeps the white text readable on any photo. Everything outside the photo cards uses theme tokens (bg-background, text-foreground, text-muted-foreground), so the section follows the site theme.