CardTrain

svganimationbackground

An endless train of tilting cards flowing along a wave — pure canvas 2D, no WebGL, fully prop-driven.

An endless train of tilting cards flowing along a wave — pure canvas 2D, no WebGL, fully prop-driven.

Playground

Every card dealt,
every promise kept

Always be
shipping.

Tweak the props in the toolbox — the preview updates in realtime. “Copy code” gives you the exact JSX for the current tweaks.

Installation

Requires the shadcn CLI. Run this from your project root — it writes the component source, then you import and go.

bash
npx shadcn@latest add "https://webcules.com/r/card-train.json"

The command installs the component source (plus its engine / type files when the component ships one). No other dependencies — everything is dependency-free canvas, SVG and CSS.

Usage

Import the component and drop it in — every prop in the toolbox is a regular React prop:

hero.tsx
import { CardTrain } from "@webcules/ui/components/ui/card-train";
export function Hero() {
return (
<section className="relative h-[600px] overflow-hidden">
<CardTrain className="absolute inset-0" />
<h1 className="absolute bottom-16 left-16 text-6xl">
Always be shipping
</h1>
</section>
);
}

Highlights

  • An endless train of overlapping cards flows along a traveling wave, each card flipping through 3D as it rides the rail — the "shipping" hero, prop-driven
  • Hand-rolled perspective projection in canvas 2D: crests sit closer and larger, troughs recede and dim toward the backdrop — no WebGL, no dependencies
  • Shadows fall card-to-card, so the stack reads as layered paper, not flat sprites; colors cycle per card from any palette you pass
  • Seamless loop (cards spawn off-canvas), deterministic seed, aria-hidden, one settled frame under prefers-reduced-motion

Example — Candy — the reference look

Rose / periwinkle / lilac cards on warm paper-gray. Compose the copy like the train wants it: headline bottom-left, tagline top-right.

example.tsx
<CardTrain
colors={["#f4a7b3", "#a9b6e6", "#d9aee0"]}
background="#e8e6e2"
/>

Example — Sunset — warmer, friendlier

Coral / amber / rose cards on warm cream — same geometry, different mood.

example.tsx
<CardTrain
colors={["#f2977f", "#f2c14e", "#e88aa0"]}
background="#faf3ea"
/>

Example — Midnight — dark heroes

Indigo / steel-blue / violet cards with brighter gradients on deep navy. Bump depth so the crest scale carries the dark scene.

example.tsx
<CardTrain
colors={["#5b6bd6", "#7f9bd9", "#9a7fd6"]}
background="#12141f"
depth={0.35}
/>

Props

PropTypeDefaultDescription
colorsstring[] | string"#f4a7b3,#a9b6e6,#d9aee0"Per-card color cycle — the train walks it by index (in code also accepts an array).
backgroundstring"#e8e6e2"Backdrop the train rides on. Use "transparent" to let the page show through.
cardSizenumber150Base card edge in px — auto-scales down on narrow containers.
countnumber26Minimum cards in the stream — auto-raised to fill the width.
overlapnumber0.65Fraction of each card hidden by the next — high values read as one ribbon.
amplitudenumber0.18Wave height as a fraction of the container height.
wavelengthnumber1.4Wave length as a fraction of the container width.
speednumber0.12Stream speed in container-widths per second; 0 = frozen.
tiltnumber1Flip intensity — 0 rides a flat rail, 1 is the cinematic default.
depthnumber0.25Perspective amount — crest scale plus trough dimming.
confettibooleantrueDrifting square-shard ambience layer around the train.
confettiCountnumber24How many confetti squares drift.
seednumber1Deterministic confetti field — same seed, same sky.

Notes

  • colors takes a comma-separated string (as the toolbox emits) or a real array — same result.
  • The wave itself glides slowly forward; the train rides it, so cards bob gently as they travel — speed={0} freezes both.
  • cardSize auto-scales down on narrow containers (capped at ~10% of the width) so the train stays proportional on phones.
  • Confetti mostly sits behind the train; a few squares drift in front to keep the scene airy.
  • Under prefers-reduced-motion it renders one settled static frame; the loop pauses on hidden tabs.