Expandable Card
InteractiveA card that spring-expands in place to reveal a body whose children fade and slide in with a slight stagger, while siblings in a grid stay put. Works controlled or uncontrolled with full button/aria-expanded keyboard semantics, and expands or collapses instantly under prefers-reduced-motion.
Inspired by Cult UI — Expandable Card (free, MIT) — independently built, not affiliated. Attribution policy
Installation
npx shadcn@latest add https://parable-three.vercel.app/r/expandable-card.jsonRuns the shadcn CLI — the component source is copied intocomponents/parable/expandable-card.tsx. You own it from there.
Usage
The exact code rendering the preview above.
<div className="w-full max-w-sm">
<ExpandableCard
title="Aurora Engine"
subtitle="Real-time gradient renderer"
defaultExpanded
media={<Sparkles />}
>
<p>A GPU-cheap animated backdrop built from drifting radial bands — transform and opacity only, so it stays buttery at 60fps.</p>
<div className="flex gap-2">
<span className="rounded-full bg-white/10 px-2.5 py-1 text-xs text-white/70">WebGL</span>
<span className="rounded-full bg-white/10 px-2.5 py-1 text-xs text-white/70">Reduced-motion safe</span>
</div>
</ExpandableCard>
</div>Props
ExpandableCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | React.ReactNode | — | Primary heading shown in the collapsed row. |
| subtitle | React.ReactNode | — | Optional secondary line under the title. |
| media | React.ReactNode | — | Decorative media — a left thumbnail or a full-width top strip. |
| mediaLayout | "thumb" | "strip" | "thumb" | Where `media` renders. `"thumb"` = left of the title; `"strip"` = above it. |
| accent | string | "#8b5cf6" | Accent used for the chevron and focus ring. Mirrors the site `--pb-violet` token. |
| defaultExpanded | boolean | false | Uncontrolled initial open state. |
| expanded | boolean | — | Controlled open state. Pair with `onExpandedChange`. |
| onExpandedChange | (expanded: boolean) => void | — | Fires with the next open state when the header is toggled. |
| children* | React.ReactNode | — | Expanded body content. Direct children fade/slide in with a slight stagger. |
npm dependencies
- motion