PParable
All components

Orbit Card Stack

Interactive

A draggable card deck (testimonials, photos, prompts): the top card can be flung left or right to spring it off-screen and orbit to the back, while prev/next buttons and the arrow keys advance and rewind through a depth-stacked pile. Exposes real carousel semantics with a polite live region and one exposed slide at a time; under prefers-reduced-motion it reorders instantly with no drag or throw physics.

Inspired by Componentry — Orbit Card Stack (free, MIT) — independently built, not affiliated. Attribution policy

Flick the top card and it arcs off-screen with a proper spring throw, then orbits to the back.

Ada Lovelace
Head of Design
Card 1 of 3

Installation

npx shadcn@latest add https://parable-three.vercel.app/r/orbit-card-stack.json

Runs the shadcn CLI — the component source is copied intocomponents/parable/orbit-card-stack.tsx. You own it from there.

Usage

The exact code rendering the preview above.

<div style={{ width: "100%", maxWidth: 320, margin: "0 auto" }}>
    <OrbitCardStack
      accent="#8b5cf6"
      cards={[
        { q: "Flick the top card and it arcs off-screen with a proper spring throw, then orbits to the back.", a: "Ada Lovelace", r: "Head of Design", c: "#8b5cf6" },
        { q: "Drag, buttons, and arrow keys all land on the same clean motion. Shipped it in an afternoon.", a: "Grace Hopper", r: "VP Engineering", c: "#ec4899" },
        { q: "The reduced-motion fallback stays fully legible. Rare to see it done right.", a: "Alan Turing", r: "Principal", c: "#22d3ee" },
      ].map((t, i) => (
        <div key={i} className="flex h-full flex-col justify-between">
          <div className="flex gap-1" style={{ color: t.c }} aria-hidden>
            {[0, 1, 2, 3, 4].map((s) => (
              <Star key={s} size={13} fill="currentColor" strokeWidth={0} />
            ))}
          </div>
          <p className="text-[15px] leading-relaxed text-white/90">{t.q}</p>
          <div>
            <div className="text-sm font-semibold text-white">{t.a}</div>
            <div className="text-xs text-white/50">{t.r}</div>
          </div>
        </div>
      ))}
    />
  </div>

Props

OrbitCardStackProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "children">

PropTypeDefaultDescription
cards*React.ReactNode[]Cards to stack, front-most first. Each is placed inside the deck surface.
onChange(index: number) => voidFires with the new front-card index whenever the deck advances or rewinds.
loopbooleantrueWrap at the ends — dismissing the last card returns to the first. Default true.
accentstring"#8b5cf6"Accent for the swipe hint, focus rings, and the active control. Defaults to Parable violet `#8b5cf6` (mirrors the site's `--pb-violet` token).
labelstring"Card stack"Accessible name for the carousel region.

npm dependencies

  • motion
  • lucide-react