Postcard Pile Hero

Postcards from Ostria

Wishyouwerehere.Youcouldbe.

A few moments our guests sent home last summer. Twelve rooms, one cove and a kitchen that cooks whatever the boats bring in.

A breakfast tray with coffee, figs and yogurt on a linen bed
Breakfast in room four
A small wooden boat anchored in a clear turquoise cove
The boat, the third cove
A stone courtyard with an ancient olive tree and a round bench
The courtyard at eleven
A couple seen from behind on a white terrace wall watching the sea at sunset
Our last evening
About this block

Postcard Pile HeroPRO

A hotel hero with a loose pile of photo postcards beside a serif headline: each card sits at its own gentle tilt with a handwritten-feeling serif caption, and every few seconds, or on a tap, the top card slides away and tucks in at the back of the pile. Hovering pauses the shuffle.

Hotel21: Postcard Pile Hero

A serif headline staggers in word by word beside a tilted pile of photo postcards, and every few seconds (or on a click) the top card slides off to the left and tucks in at the back of the pile.

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.

Upgrade Now

Installation

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

bash
npx shadcn add "https://beste.dev/r/hotel21?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://beste.dev/r-base/hotel21?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/hotel21.tsx plus the button22 sliding-marker pill for the booking button and the text1 word stagger for the headline.

Quick start

The installed file exports hotel21Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.

tsx
import { Hotel21, hotel21Demo } from "@/components/beste/block/hotel21";

export default function Page() {
  return <Hotel21 {...hotel21Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Hotel21 } from "@/components/beste/block/hotel21";

export default function Page() {
  return (
    <Hotel21
      eyebrow="Postcards from Ostria"
      heading="Wish you were here. You could be."
      description="Twelve rooms, one cove and a kitchen that cooks whatever the boats bring in."
      button={{ label: "Book a stay", href: "/book", tone: "dark" }}
      link={{ label: "Read the guest book", href: "/guest-book" }}
      cards={[
        {
          caption: "Breakfast in room four",
          image: { src: "https://oud.pics/b/hotel/ostria-breakfast-tray.webp", alt: "A breakfast tray with coffee, figs and yogurt" },
        },
        {
          caption: "The boat, the third cove",
          image: { src: "https://oud.pics/b/hotel/ostria-boat-cove.webp", alt: "A small wooden boat in a turquoise cove" },
        },
        {
          caption: "Our last evening",
          image: { src: "https://oud.pics/b/hotel/ostria-couple-sunset.webp", alt: "A couple watching the sea at sunset" },
        },
      ]}
      interval={6}
      labels={{ next: "Show the next postcard" }}
    />
  );
}

Props

PropTypeDefaultDescription
eyebrowstring–Small line above the heading
headingstring–Serif <h1>, staggered word by word through Text1
descriptionstring–Paragraph under the heading
buttonActionButton–Primary pill action, dark tone when none is set
linkTextLink–Text link with an arrow next to the button
cardsPostcard[][]The postcards in the pile, first one on top
intervalnumber5Seconds between automatic shuffles
labelsLabels{}Accessible label for the clickable pile
classNamestring–Extra classes for the outer <section>
ts
type ActionButton = {
  label: string;
  href: string;
  tone?: "primary" | "light" | "dark" | "outline";
};

type TextLink = {
  label: string;
  href: string;
};

type Postcard = {
  caption: string;
  image: { src: string; alt: string };
};

type Labels = {
  next?: string;
};

Behavior notes

  • On mount the cards rise in from below with an exaggerated tilt and settle into a fixed rotation per card (cycled from six preset angles), each deeper card offset 10px lower and scaled down 3.5 percent; cards beyond the fourth in the pile are faded out.
  • Advancing slides the top card 70 percent to the left with a stronger counter-tilt over 650ms, then moves it to the back of the order. A new advance is ignored while a card is still leaving.
  • The pile shuffles on its own every interval seconds; hovering pauses the timer, and with reduced motion the auto-shuffle is off entirely (a click still swaps cards, instantly).
  • The pile is a role="button" element that also advances on Enter or Space, with labels.next as its accessible name.
  • Fewer than two cards disables both the timer and manual advancing.
  • The layout is a single stack below the large breakpoint, where the copy takes six of twelve columns and the pile sits in the right five.

More Hotel blocks

View all Hotel
PRO

hotel36

Portrait Film Split Hero

PRO

hotel38

Films and Photos Carousel Hero

PRO

hotel35

Watch the Film Hero

Get Pro