caliper uidocs

Hero signup

Block

A hero with a sign-up form built in.

Installs components/ui/hero-signup.tsxDepends on @radix-ui/react-slot, lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/hero-signup.json

Early access

Build calmer products, faster

Join the waitlist for a component library that treats accessibility and motion as part of the design, not an extra.

One email when we open the doors. No spam.

Joined by 2,400 designers and engineers

Try “fail@x.com” for an error and “dup@x.com” for an address already on the list.

When to use #

Use it for

  • Capturing an email at the top of the page.

Skip it when

  • Signing up needs more than a field.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/hero-signup.json

Adds its 6 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { HeroSignup } from "@/components/ui/hero-signup"

export function HeroSignupDemo() {
  return (
    <div className="grid gap-3">
    <HeroSignup eyebrow="Early access" title="Build calmer products, faster" description="Join the waitlist for a component library that treats accessibility and motion as part of the design, not an extra." people={["Maya Chen", "Ravi Patel", "Noor Haddad", "Kofi Mensah"]} proof="Joined by 2,400 designers and engineers" note="One email when we open the doors. No spam."
              onSubmit={async (email) => { await new Promise((r) => setTimeout(r, 900)); if (email.startsWith("fail")) throw new Error("x"); return email.startsWith("dup") ? "exists" : "joined"; }} />
    <p className="text-xs text-muted-foreground">Try “fail@x.com” for an error and “dup@x.com” for an address already on the list.</p>
    </div>
  );
}

API reference #

HeroSignup

component
PropTypeDefaultDescription
eyebrowstring—
title *ReactNode—
description *string—
onSubmit *(email: string) => Promise<"joined" | "exists">—Resolve "joined" or "exists", or throw to show an error.
peoplereadonly string[][]
proofstring—
notestring—
classNamestring—

Accessibility #

It exposes status semantics, with aria-hidden, aria-invalid and aria-describedby kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.