Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Progress Urgency CTA Block for Radix UI

A centred scarcity statement: a headline over a framed seat map of 200 dots, one per founding seat, with the claimed ones solid and the next open ones pulsing, then the price with its list price struck and a Shine Button.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Make scarcity something visitors can count, in this call to action for React with Radix UI. The block sits on the page's spine: two side rails at the page column and one top rule with a + mark on each crossing, so it joins the blocks above and below it. A centred headline and one sentence introduce Marrow's founding seats; under them a wide frame with + marks on its corners holds a seat map of two hundred dots, forty to a row on desktop and twenty-five on phones. The 153 already claimed are solid in the theme's foreground tone, the next few open ones pulse softly, and the rest wait, dim. A legend and the seats left sit inside the frame; below it are the price with its list price struck through, the saving worked out, and one Shine Button to claim a seat. It's built from Badge and Shine Button, with CSS animation only. The block follows the theme and fits one screen on desktop, with the button on the first screen of a phone. For founding-member offers, limited cohorts, early-access programmes and launch pricing with a hard cap.

FAQ

How do I change the number of seats and how many are taken?

Edit TOTAL_SEATS and CLAIMED_SEATS at the top of the file. The map is drawn from a list with one entry per seat, the seats left, the headline and the screen-reader summary are all worked out from those two numbers, and PULSING sets how many open seats pulse. The map is forty columns wide on desktop and twenty-five on phones, so use 200 (a multiple of both) or adjust grid-cols-* to match, or the last row will be short.

Is the count live?

No: it's a typed number, and the block never calls a server. To make it live, pass the claimed count in from your server or build (a prop, or a fetch in the page) in place of the constant; every figure and the dots follow from it.

What does a screen reader hear for the dots?

The map is a single image with a label built from the numbers ("153 of 200 founding seats claimed, 47 left"); the two hundred dots themselves are hidden from assistive technology, since two hundred empty elements would say nothing. The seats left are also written out as text under the map.

What moves, and what happens with reduced motion?

The map is drawn row by row from the top when the section loads, and the next six open seats pulse in turn with Tailwind's animate-pulse and a staggered delay. With reduced motion the map is simply there and nothing pulses. The pulse is a soft decoration that carries no information; the legend and the numbers say everything it does.

How is the saving worked out?

LIST_PRICE and FOUNDER_PRICE are the two numbers you type; the percentage in the badge is worked out from them with whole-number arithmetic, rounded down ($20 off $49 is 40%). The old price is read by screen readers as "Was $49" and the new one as ", now $29 per month".

Where does the button go?

Claim your spot is a real link (href="#"): point it at your sign-up or checkout. It is a ShineButton wrapping an a element, so it can be your router's link.