Second Address Countdown

A crowded Mediterranean beach with a yacht anchored offshore

A second address

Portoopensinthenewyear.

Two rooms above a bookshop on Rua das Flores, the same three practitioners on rotation, and the same rule: nobody is rushed.

98
Days
07
Hours
38
Minutes
22
Seconds

One email when the doors open. Nothing before, nothing after.

About this block

Second Address CountdownPRO

A full-height coming-soon page on a photograph that drifts slower than the page under a scrim and film grain: a serif headline settling in word by word, an intro, a live serif countdown to the opening date, and a rounded email field with a light sliding-marker pill.

Comingsoon80: Second Address Countdown

A full-screen opening announcement over a photograph that drifts behind film grain: the headline settles in word by word, a live countdown ticks under the description, and a rounded email capture sits opposite it.

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://ui.beste.co/r/comingsoon80?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

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

This installs the block to components/beste/block/comingsoon80.tsx, the shadcn input primitive the capture is built on, and the button22 sliding-marker pill and the text1 word stagger it uses.

Quick start

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

tsx
import { Comingsoon80, comingsoon80Demo } from "@/components/beste/block/comingsoon80";

export default function Page() {
  return <Comingsoon80 {...comingsoon80Demo} />;
}

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

tsx
import { Comingsoon80 } from "@/components/beste/block/comingsoon80";

export default function Page() {
  return (
    <Comingsoon80
      eyebrow="A second address"
      heading="Porto opens in the new year."
      description="Two rooms above a bookshop, the same three practitioners on rotation."
      opensAt="2027-01-12T09:00:00Z"
      image={{ src: "/photos/lake.jpg", alt: "A calm lake reflecting mountains at first light" }}
      form={{
        placeholder: "Your email",
        button: "Tell me when",
        note: "One email when the doors open. Nothing before, nothing after.",
      }}
      labels={{ days: "Days", hours: "Hours", minutes: "Minutes", seconds: "Seconds" }}
    />
  );
}

Props

PropTypeDefaultDescription
eyebrowstring–Small-caps line above the headline
headingstring–Headline, rendered as the page h1 through Text1
descriptionstring–Paragraph above the countdown
opensAtstring–ISO date the countdown runs toward; the countdown is skipped when unset
image{ src: string; alt: string }–Full-bleed background photograph
form{ placeholder: string; button: string; note: string }–Email capture; the whole form is skipped when unset
labelsComingsoon80Labels{}Names of the four countdown units
classNamestring–Extra classes for the outer <section>
ts
type Comingsoon80Labels = {
  days?: string;
  hours?: string;
  minutes?: string;
  seconds?: string;
};

Behavior notes

  • The countdown is computed from Date.now() on every tick rather than decremented, so a tab that was backgrounded for an hour shows the correct time the moment it wakes rather than an interval's worth of drift.
  • The remaining time is clamped at zero, so a date in the past renders 00 across all four units instead of counting up into negatives.
  • Its initial value is produced in a useState initialiser, which means the first paint already carries real numbers rather than four zeroes that snap a second later.
  • Units are padded to two digits and set in tabular-nums, so the row does not change width as the seconds roll over.
  • The heading runs with trigger="mount": a full-screen page is above the fold, so waiting for a scroll into view would leave it invisible.
  • The grain overlay's feTurbulence filter takes its id from useId, so two grain surfaces on one page do not collide on a global SVG id.
  • The Input is restyled for a dark surface (a translucent fill, a background/30 border, and the focus ring traded for a solid border), because the shadcn default is drawn for a light one.
  • The form only calls preventDefault(); no address is captured or sent until you wire it up.

Wiring the form up

This block ships the form markup only; state, validation, and submit are yours to add. Our guide wires the shadcn Field primitives to React Hook Form, TanStack Form, and Formisch on one field system.

More Coming Soon blocks

View all Coming Soon
PRO

comingsoon81

Quiet Waitlist

PRO

comingsoon77

Full-Bleed Launch Countdown

PRO

comingsoon49

Framed Editorial Launch

Get Pro access