A full-height hotel hero that crossfades through the hours of a stay: each photograph drifts in slowly behind a serif headline, while a row of four labelled hairlines fills as the slide plays, pauses on hover and lets guests jump to morning, noon, dusk or night.
A full-height hero that crossfades between photographs of different hours while each one slowly zooms out, under a serif headline and a row of labeled hairlines that fill as the current slide plays.
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://beste.dev/r/hotel2?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://beste.dev/r-base/hotel2?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/hotel2.tsx plus the button22 sliding-marker pill used for the actions and the text1 word stagger behind the headline.
The installed file exports hotel2Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Hotel2, hotel2Demo } from "@/components/beste/block/hotel2";
export default function Page() {
return <Hotel2 {...hotel2Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Hotel2 } from "@/components/beste/block/hotel2";
export default function Page() {
return (
<Hotel2
eyebrow="A house on the Aegean"
heading="The day moves slowly here."
description="Morning in linen, a long lunch in the shade and the pool at dusk."
buttons={[
{ label: "Book a stay", href: "/book", tone: "light" },
{ label: "See the rooms", href: "/rooms", tone: "outline" },
]}
slides={[
{
label: "Morning",
caption: "The sea suite, curtains open",
image: { src: "https://oud.pics/b/hotel/ostria-sea-suite.webp", alt: "A linen bed facing an arched window onto the sea" },
},
{
label: "Dusk",
caption: "The pool holds the last of the light",
image: { src: "https://oud.pics/b/hotel/ostria-pool-dusk.webp", alt: "An infinity pool meeting the sea at dusk" },
},
]}
interval={7}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Uppercase line above the heading |
heading | string | – | Serif <h1>, staggered word by word through Text1 |
description | string | – | Paragraph under the heading |
buttons | ActionButton[] | [] | Pill links under the description |
slides | Slide[] | [] | Background photographs with their timeline label and caption |
interval | number | 7 | Seconds each slide stays before the next one |
className | string | – | Extra classes for the outer <section> |
type ActionButton = {
label: string;
href: string;
tone?: "primary" | "light" | "dark" | "outline";
};
type Slide = {
label: string;
caption: string;
image: { src: string; alt: string };
};interval plus the crossfade, so the photo is still moving when the next one fades in.interval seconds and loops. It stops while the pointer is over the section, and does not advance at all with reduced motion or when there are fewer than two slides.interval seconds. When paused it jumps to full, and when the pointer leaves the countdown restarts.aria-current when active. Inactive labels are dimmed and brighten on hover.light tone when tone is omitted, and the eyebrow, description and buttons fade in after the headline, timed by its word count.