Progressive Card Reveal

A vertical stack of cards where one card expands to its full view while the rest collapse to compact pills, animating the morph between them.

A vertical stack of cards. Exactly one card is expanded at a time — the rest collapse to compact pills. The parent owns which card is active; clicking a collapsed pill asks it to switch, and Framer Motion animates the size morph and crossfades the content.

Walking

Distance

2 miles

Time

~40 minutes

Installation

pnpm dlx shadcn@latest add @godui/progressive-card-reveal

Requires the @godui namespace in your components.json (one-time setup):

{  "registries": {    "@godui": "https://godui.design/r/{name}.json"  }}

Usage

import { ProgressiveCardReveal } from "@/components/godui/progressive-card-reveal";

ProgressiveCardReveal is a compound component. The root is controlled via activeIndex — keep it in state and update it from onActiveChange. Each Card declares two views with the CardCollapsed and CardExpanded slots; the card derives its index from its position, so you never pass an index manually.

const [active, setActive] = useState(0);

<ProgressiveCardReveal activeIndex={active} onActiveChange={setActive}>
  <ProgressiveCardReveal.Card>
    <ProgressiveCardReveal.CardCollapsed>Flight — ~3 hours</ProgressiveCardReveal.CardCollapsed>
    <ProgressiveCardReveal.CardExpanded>{/* full content */}</ProgressiveCardReveal.CardExpanded>
  </ProgressiveCardReveal.Card>
  <ProgressiveCardReveal.Card>
    <ProgressiveCardReveal.CardCollapsed>Driving — ~18 hours</ProgressiveCardReveal.CardCollapsed>
    <ProgressiveCardReveal.CardExpanded>{/* full content */}</ProgressiveCardReveal.CardExpanded>
  </ProgressiveCardReveal.Card>
</ProgressiveCardReveal>

Clicking a collapsed card fires onActiveChange(index). Clicking the already-expanded card is a no-op — one card stays open at all times. The transition respects prefers-reduced-motion, switching instantly when reduced motion is requested.

Props

ProgressiveCardReveal

PropTypeDefaultDescription
activeIndexnumberIndex of the expanded card (controlled).
onActiveChange(index: number) => voidFired when a collapsed card is activated.
maxDepthnumberCaps how far collapsed cards narrow. Cards more than maxDepth steps from the active one all share the width at maxDepth. Omit for unbounded funneling.

Also accepts all div attributes (e.g. className).

ProgressiveCardReveal.Card

Accepts all div attributes. Children must be a CardCollapsed and a CardExpanded slot.

ProgressiveCardReveal.CardCollapsed / CardExpanded

Slot wrappers — their children are the collapsed (pill) and expanded views.

On this page