Tour
A guided walk over a page that already exists: the three things a new reader has to be shown once, pointed at where they actually are.
import { Tour } from 'neba';
<Tour
open={open}
onOpenChange={setOpen}
steps={[
{ target: '#search', title: 'Find anything', content: 'Everything is behind this field.' },
{ target: '#deploy', title: 'Ship it', content: 'Builds the current branch.', side: 'left' }
]}
/>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| steps * | readonly TourStep[] | — | The stops, in order |
| open | boolean | — | Whether the tour is running. With onOpenChange it makes it controlled |
| defaultOpen | boolean | false | Whether it starts running, uncontrolled |
| onOpenChange | (open: boolean) => void | — | Fired whenever it starts or ends |
| step | number | — | Which stop, from 0 |
| defaultStep | number | 0 | Which one it starts on, uncontrolled |
| onStepChange | (step: number) => void | — | Fired whenever the step changes |
| onFinish | () => void | — | Called when the last step's button is pressed, before the tour closes |
| mask | boolean | true | Dims the page and cuts the target out of the dimming. The dimming never takes the pointer |
| skippable | boolean | true | Draws the Skip button beside the counter |
| dismissible | boolean | true | Whether Escape ends the tour |
| scrollIntoView | boolean | true | Scrolls each target into view as the tour reaches it |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The card's type scale and how wide it may get |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | The role the card's edge and buttons carry |
| densityshared | 'default' | 'compact' | 'default' | Changes the card's inset and nothing else |
| locale | string | — | BCP 47 tag the buttons and the counter are written in. Unsupported tags fall back to English |
| previousLabel | ReactNode | — | The Previous button |
| nextLabel | ReactNode | — | The Next button |
| doneLabel | ReactNode | — | What Next becomes on the last step |
| skipLabel | ReactNode | — | The Skip button |
| className | string | — | Class names for the card |
| classNames | NebaSlots<'mask' | 'title' | 'description' | 'close' | 'footer'> | — | Class names for the parts behind the root. The root itself is className, so there is no root key |
TourStep
| Prop | Type | Default | Description |
|---|---|---|---|
| target | string | — | A CSS selector for what this step is about. Left out, the step is centred with nothing cut out |
| title | ReactNode | — | The step's heading |
| content | ReactNode | — | What it says |
| sideshared | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Which edge of the target the card sits on |
| alignshared | 'start' | 'center' | 'end' | 'center' | Where along that edge |
| padding | number | 6 | How far the cut-out is inflated past the target, in pixels |
It is HowToSteps turned inside out. That component puts the instructions in the page and the reader follows them; this one leaves the page as it is and stands over it. The steps are therefore given by selector: what a tour is about is already on screen, and describing it a second time inside the card would be two copies to keep in step.
Examples
steps · target
Each step names its target with a CSS selector, read against the page as it is at that moment. A step with no target is centred with nothing cut out: which is what a welcome step and a closing step are.
open · step
open runs the tour and step is which stop it is on; both have uncontrolled counterparts and both report their changes. onFinish is called when the last step's button is pressed, before the tour closes.
mask
Dims the page and cuts the target out of the dimming. The dimming never takes the pointer, so the control being pointed at can still be used: which is the difference between a tour and a sequence of dialogs.
locale and the labels
The buttons and the counter come from locale. previousLabel, nextLabel, doneLabel and skipLabel write any of them out instead.
className · classNames
className lands on the card: the popup each step is written on. The dimming behind it is a sibling of that popup rather than a descendant, so it is reached through classNames.mask and no other way.
<Tour
steps={steps}
className="max-w-sm"
classNames={{ mask: 'bg-black/70', footer: 'justify-between' }}
/>The slots are mask, title, description, close and footer. See prop conventions for how a class name you pass resolves against the component's own.
Accessibility
- The card is a dialog named by its title and described by its content, and focus moves into it as each step opens.
- Escape ends the tour unless
dismissibleis off. A press outside it does not: using the page is what the tour is for. - A tour is never the only way to something. Whatever it points at has to be findable without it: a reader who dismissed it, or never saw it, gets no second showing.