Card Loader
A stack of three cards that cycles while something takes a few seconds to load.
import { CardLoader } from "@/ui";Card Loader matches the Figma Card Loader Animation. Use it to fill a panel while something takes several seconds, like building an analysis plan, by showing short tips about the product. In the app it is shown in the workbook chat's plan panel while the plan is getting ready and while modifications are applied.
Default
Three cards are stacked, the front one largest. Every 3 seconds the front card drops to the back and the next card grows into its place. The rail on the left shows which card is in front. The buttons on the right go to the previous card, pause or resume, and go to the next card.
<CardLoader
cards={[
{ title: "Understanding your Data", description: "See what data is available...", image: dataPreview },
{ title: "Understanding Key Definitions (KDs)", description: "Standardize how metrics...", image: kdPreview },
{ title: "Petavue Visualization", description: "Create trustworthy charts...", image: vizPreview },
]}
/>The first card in cards starts in front. Each card needs a unique title.
With a status line
Card Loader doesn't say what is loading. Put a short status line with a Spinner under it, as the workbook chat does.
<CardLoader cards={tips} />
<div className="status">
<Spinner size={16} />
Getting ready...
</div>Without images
A card without an image shows a Grey 100 block in its place. Use it for a draft before the screenshots exist. This example also sets interval to 4 seconds.
<CardLoader cards={tipsWithoutImages} interval={4000} />Anatomy
| Part | Size | Style |
|---|---|---|
| Frame | 500 × 378px | Shrinks to its container's width |
| Front card | 400px wide, 40px from the top | White, image 234px |
| Next card | 364px wide, 20px from the top | White, 269px tall, image 163px |
| Back card | 304px wide, at the top | Grey 50, 206px tall, image hidden behind a Grey 100 block 100px tall |
| Card | 12px padding, 10px gap | 1px Grey 100 border, 8px radius, 0 8px 4px shadow at 8% |
| Title | 16px / 26px, medium | --color-text-primary, one line, cut off with an ellipsis |
| Description | 12px / 19px | --color-text-secondary |
| Progress rail | 6px wide, 6px gap | Primary 100 dots; the current card is a 60px Primary 500 bar |
| Buttons | 32px round, 8px gap | Grey 50 fill, 1px Grey 100 border, 12px Grey 600 icon; Grey 100 on hover |
Card images are anchored to the top left and cropped to fill the space, so the top left of a screenshot is what shows. Use screenshots about 2:1 wide.
Cards move with a 0.6s ease. With reduced motion turned on, they switch without moving.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
cards | { title, description, image? }[] | [] | Three cards. Nothing renders when it is empty. |
interval | number | 3000 | Milliseconds each card stays in front. |
label | string | "Product tips" | Accessible name of the carousel. |
className | string | Other props are passed to the root too. |
Card Loader keeps its own place and pause state. It starts again from the first card each time it mounts.
Differences from Figma
- Figma has no state after Pause is pressed. Here the Pause button turns into a Play button.
- Figma has no hover state for the buttons. Here they turn Grey 100.
- The back card's fill is Figma's #FCFCFC, which isn't a token. It uses Grey 50 (#F8F9FC).
Accessibility
- The root is a
sectionwitharia-roledescription="carousel"and thelabelas its name. Only the front card is exposed to screen readers; the other two arearia-hidden. - While cards change by themselves, changes aren't announced. Once paused, Previous and Next announce the new front card.
- The Pause button stops the cycle, which people need for anything that moves for more than 5 seconds. Keep it.
- Images are decorative (
alt=""). The title and description carry the content.