Components / Data display

CardStack

Review a deck one card at a time, with a throw and an undo.

Install

npx shadcn@latest add https://irisui.dev/r/card-stack.json
import { CardStack } from "@/components/iris/card-stack/card-stack";
Registry item card-stackBrings in foundationRegistry JSON

Preview

CardStack

A deck of cards reviewed one at a time: throw the top card left or right, or use the buttons and arrow keys, with undo.

When to use

  • Quick, one-by-one triage where each item deserves a moment: shortlisting firms for a raise, rooms to view, or design options.
  • When the decision is binary (pass / keep) and order of review does not matter.
  • Use a list or SortableDataTable when people need to compare items side by side, and Carousel to browse without deciding.

Usage

import { CardStack } from "@/components/iris/card-stack/card-stack";

h(CardStack, {
  items: firms,
  getKey: (firm) => firm.id,
  getLabel: (firm) => firm.name,
  renderCard: (firm) => h(FirmFace, { firm }),
  label: "Firms for the seed raise",
  labels: { left: "Pass", right: "Shortlist" },
  outcomes: { left: "passed", right: "shortlisted" },
  onDecide: (firm, side) => save(firm.id, side),
  renderEmpty: (reset) => h(Button, { onClick: reset }, "Start over"),
});

Props

Prop Type Default Description
items T[] — (required) Cards in order; the first is on top.
getKey (item) => string — (required) Stable key.
getLabel (item) => string — (required) Short name used in the card's label and announcements.
renderCard (item) => ReactNode — (required) Card content. The stack draws the surface (border, radius, background); you fill it.
onDecide / onUndo / onReset callbacks — (item, "left" | "right") for decide and undo; onReset() when all cards come back.
labels { left: string; right: string } { left: "Pass", right: "Keep" } Button labels and the stamps shown while dragging.
outcomes { left: string; right: string } — Past tense for announcements ("skipped", "shortlisted").
label string "Cards" Accessible name for the stack.
renderEmpty (reset) => ReactNode — Shown once every card is decided; receives a function that brings all cards back. Defaults to "All cards reviewed" with a Start over button.
className string — Extra class on the root.

Notes

  • Keyboard: Left/Right decide, Backspace or Cmd/Ctrl+Z undoes. Instructions are provided as a visually hidden hint and each decision is announced in a polite live region.
  • Card height is fixed by the --card-height custom property (19rem); keep content within it.
  • Under reduced motion cards fade instead of flying, and undone cards fade back in at home.