Tour
A guided tour over a real screen: the page dims, what is being explained stays lit and usable, a TourCard points at it, and a cursor can act the step out. A tour is a list of steps; the motion is the kit's and is the same in every tour.
Anatomy
Three layers over the screen, all from the kit. Start the sample below to see them.
- 1Cut-out
The page dims with the kit's overlay tone and the step's target stays lit and clickable. Within one scrolling area it springs to the next target; into another view it closes where it was and opens where it lands. It takes the corner of what it lights and stops at the edge of the area it is seen through. Over a modal dialog it is not drawn: the dialog's own scrim already sets it apart.
- 2Card
A TourCard placed by a Popover beside the target. Skip on the first step, Back from the second, the X always closes. It leaves while the light moves and arrives once it has landed, and goes to the bottom-right corner when no side of the target has room.
- 3Cursor
Optional, per step. It acts the step's demo out on the real screen: it goes somewhere, points, clicks, types or drags, and what it rests on is marked data-tour-hover. The demo plays on arrival and once more after a pause, unless the step plays it once.
Recipes
A HighlightDialog is the door: its Primary starts the tour, Skip tour closes it. The steps point at data-tour marks on the screen. onRestore puts the screen back when a step is left.
import { Tour, type TourStep } from "@/components/patterns/tour";
import { HighlightDialog } from "@/components/patterns/highlight-dialog";
// Mark the screen: <aside data-tour="methods">, <div data-tour="publish">
const STEPS: TourStep[] = [
{
target: "methods",
side: "right",
title: "Your payment methods",
body: "Switch a method on or off, and open it with Edit.",
demo: [
{ do: "click", to: { tour: "methods", find: '[role="switch"]', nth: 3 } },
{ do: "wait", ms: 900 },
{ do: "point", to: { tour: "methods", find: "button:not([role])", nth: 0 } },
],
},
{
target: "publish",
side: "bottom",
align: "end",
title: "Nothing goes live until you publish",
body: "Your changes stay as a draft until you review and publish them.",
// Publish is pointed at, never pressed.
demo: [{ do: "point", to: { tour: "publish", find: "button", nth: 0 } }],
},
];
const [welcome, setWelcome] = React.useState(true);
const [tour, setTour] = React.useState(false);
<HighlightDialog
open={welcome}
onOpenChange={setWelcome}
eyebrow="New"
title="The Checkout Builder has a new layout"
description="Your payment methods, a preview of the checkout and Publish, all on one page."
primaryLabel="Show me around"
secondaryLabel="Skip tour"
onPrimary={() => { setWelcome(false); setTour(true); }}
/>
<Tour steps={STEPS} open={tour} onClose={() => setTour(false)} onSnapshot={snapshot} onRestore={restore} />The smallest announcement. One step, one card with Got it!, and a demo if showing beats telling.
const steps: TourStep[] = [
{
target: "methods",
side: "right",
title: "Reorder your payment methods",
body: "Drag a method to change where shoppers see it.",
demo: [
{
do: "drag",
to: { tour: "methods", find: "[data-grip]", nth: 3 }, // what the cursor grabs
lift: { tour: "methods", find: "[data-row]", nth: 3 }, // what travels
onto: { tour: "methods", find: "[data-row]", nth: 1 }, // where it lands
apply: () => setOrder(moved), // the real reorder
},
{ do: "wait", ms: 900 },
],
},
];
<Tour steps={steps} open={open} onClose={() => setOpen(false)} onRestore={() => setOrder(start)} />Import
Copy this import line at the top of the file where you compose this molecule.
import { Tour, type TourStep } from "@/components/patterns/tour";Props: Tour
| Prop | Type | Default | Description |
|---|---|---|---|
| steps | TourStep[] (required) | — | The tour, in order. |
| open | boolean (required) | — | Whether the tour is running. The tour remembers nothing: who has seen it is yours to keep. |
| onClose | (reason) => void (required) | — | "completed" from the last step's button; "dismissed" from Skip, the X or Esc. |
| onSnapshot | () => void | — | Called right before a step's demo starts acting: the moment to remember how the screen is. |
| onRestore | () => void | — | Called when a step whose demo acted is left, and before a demo plays again: put the screen back. |
| labels | object | — | next, done, skip, back, close, and step(current, total) for the count. Defaults are in English. |
Props: A step
| Prop | Type | Default | Description |
|---|---|---|---|
| target | string (required) | — | A data-tour value on the screen. The cut-out and the card follow it. |
| title | string (required) | — | A few words. |
| body | React.ReactNode (required) | — | One sentence. |
| side | "top" | "right" | "bottom" | "left" | "right" | Where the card goes. Another side is used when this one has no room. |
| align | "start" | "center" | "end" | "start" | How the card lines up along that side. |
| before | () => void | — | Runs when the step becomes current: take the screen to the view the step points at. |
| demo | TourDemoAction[] | — | What the cursor acts out on this step. |
| once | boolean | false | Play the demo once, not again after a pause: for a demo whose end is the point, which a second play would undo. |
Props: A demo action
| Prop | Type | Default | Description |
|---|---|---|---|
| move | { to } | — | Go to an element and stay. |
| point | { to } | — | Go to it, pulse and tint it for a moment, without pressing it. |
| click | { to, act? } | — | Go to it, rest, press. The click is real; with act, the press is shown and act runs instead. |
| type | { to, text } | — | Go to a field and type into it, a character at a time. |
| drag | { to, onto, lift?, apply } | — | Pick something up by its grip, carry it onto another element and call apply to make the reorder real. |
| run | { fn } | — | Change the screen without the cursor, to set up the next action. It belongs here and not in before, so it does not happen when the demo does not play. |
| wait | { ms, until? } | — | Hold still so the result is seen; with until, hold until that element is on the screen, for at most ms. |
| to (locator) | string | { tour, find, nth? } | { css, nth? } | () => element | — | A data-tour value; an element inside one; anything on the page, for menus and dialogs that render elsewhere; or a function as a last resort. |
When to use
- To walk someone through a new layout or a new way of doing something, in 3 to 5 steps.
- For a one-step tip on one control.
- When acting a step out shows more than a sentence can say.
When not to use
- To announce one feature with an image: use HighlightDialog on its own.
- For help that should always be there: use Tooltip.
- For more than five steps: split it, or let the screen explain itself.
Usage
- Start it from a door the person chooses, not by itself over a task.
- One idea, one sentence and one demo per step.
- Give it an onRestore, so a demo never leaves the screen changed.
- Find elements by data-tour, not by their text.
- Don't have a demo click Publish, Save or Delete. Point at it, or show the outcome with act.
- Don't rely on the demo alone: under reduced motion it does not play.
- Don't change its motion per tour: the timing is the kit's.
- Don't type real-looking personal or card data.
Related
Cross-links to the atoms this molecule composes and sibling patterns.
- Announcing what's newWhich size of announcement to use, and how to write a tour.
- Tour cardThe card of each step.
- Highlight dialogThe door to a tour.
- PopoverWhat places the card.
- MotionThe tour's timing, listed under Outside the tokens.