Tour card
The card of a feature tutorial or a tip: a title with a close X, one short line of body, and a footer with Skip or Back, the step count and the primary action. It is the design system's Tutorial, the card production ships, fed from plain props.
Anatomy
One card, 368px at most: rounded-md, a 1px border, the popover surface, shadow-md and p-4. The kit wraps the design system's component and restyles nothing.
- 1Title
text-base, medium. A few words: what this step is about.
- 2Close
A ghost icon button, top right. It always closes the whole thing. Hide it with hideClose.
- 3Body
text-sm, muted. One sentence. If it needs two, it is two steps.
- 4Skip or Back
Outline, on the left. Skip on the first step; with onBack, Back from the second one on. The design system hides it on the last step.
- 5Step count
A dot per step in the middle, the current one in primary; progress="text" shows "Step 2 of 4" instead. Only when there is more than one step. Pass stepLabel to translate it (with dots, a screen reader hears it).
- 6Primary
On the right: Next, and Got it! on the last step.
States
Skip on the first step, Back from the second one on, the X always closes. Press Next to walk the three steps.
Skip on the left, the count in the middle, Next on the right.
<TourCard title="Your payment methods" body="Switch a method on or off, and open it with Edit." step={1} total={3} />No left button, as the design system draws it. The primary reads Got it! and finishes.
<TourCard title="Nothing goes live until you publish" body="Every change is listed before it reaches shoppers." step={3} total={3} />One step: no count and no Skip, only Got it! and the X.
<TourCard title="Customize your table" body="Now you can rearrange, hide and resize the columns." />Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
The card does not place itself. Put it in a Popover anchored to what it explains, and take the popover's own surface off so the two do not stack.
function TutorialPopover() {
const [open, setOpen] = React.useState(false);
const [step, setStep] = React.useState(1);
const total = 3;
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline">Show tutorial</Button>
</PopoverTrigger>
{/* The card brings its own surface; the popover only places it. */}
<PopoverContent align="start" className="w-92 border-0 bg-transparent p-0 shadow-none">
<TourCard
title="Tutorial title"
body="Say what the new feature does in one sentence."
step={step}
total={total}
onNext={() => (step < total ? setStep(step + 1) : setOpen(false))}
onBack={() => setStep(step - 1)}
onDismiss={() => setOpen(false)}
/>
</PopoverContent>
</Popover>
);
}The same composition with one step. Got it! and the X both close it.
function TableTip() {
const [open, setOpen] = React.useState(false);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline">Show tip</Button>
</PopoverTrigger>
<PopoverContent align="start" className="w-92 border-0 bg-transparent p-0 shadow-none">
<TourCard
title="Customize your table"
body="Now you can rearrange, hide and resize the columns."
onNext={() => setOpen(false)}
onDismiss={() => setOpen(false)}
/>
</PopoverContent>
</Popover>
);
}Import
Import it from the kit's ui path, never from the design system directly. That file also loads the icon font the card's close X needs.
import { TourCard } from "@/components/ui/tour-card";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string (required) | — | The step's title, and the card's accessible name. |
| body | React.ReactNode (required) | — | One sentence under the title. |
| step | number | 1 | This step's position, starting at 1. |
| total | number | 1 | How many steps. The count and the left button only show when there is more than one. |
| onNext | () => void | — | The primary action: go to the next step, or finish on the last one. |
| onDismiss | () => void | — | The close X and Skip. Production treats both as the same event. |
| onBack | () => void | — | Go to the previous step. When set, the left button is Skip on the first step and Back after it. |
| hideClose | boolean | false | Hide the close X. |
| nextLabel / doneLabel / skipLabel / backLabel / closeLabel | string | "Next" / "Got it!" / "Skip" / "Back" / "Close" | The labels. The design system has no translations of its own. |
| stepLabel | string | "Step X of N" | The count, already written in the page's language. |
| progress | "dots" | "text" | "dots" | How the count shows: a dot per step, or the "Step X of N" text. |
| width | number | 368 | Maximum width in px. |
When to use
- To explain a new feature in a few steps, each pointing at one thing on the screen.
- For a one-step tip on a single control.
- Whenever a prototype needs the tutorial card production already has.
When not to use
- To announce a feature with an image and a way to go see it: that is a dialog, not a card.
- For help that should always be available on hover: use Tooltip.
- For an error or a warning: use Alert.
Usage
- One idea per step, in one sentence.
- Keep a way out on every step: the X, and Skip on the first one.
- Pass onBack so a step can be read again.
- Translate the labels and the step count.
- Don't restyle it: it is the design system's card.
- Don't put a second action inside the body.
- Don't run more than five steps.
- Don't let it float without pointing at something.
Related
Cross-links to atoms and patterns you may reach for next.