OverlaysReact
A guided tour of a page: a popup that walks through steps, one at a time, each anchored to the element it explains.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<Tour.Root>
<Tour.Trigger />
<Tour.Portal>
<Tour.Backdrop />
<Tour.Spotlight />
<Tour.Positioner>
<Tour.Popup>
<Tour.Arrow />
<Tour.Progress />
<Tour.Close />
<Tour.Title />
<Tour.Description />
<Tour.Skip />
<Tour.Previous />
<Tour.Next />
</Tour.Popup>
</Tour.Positioner>
</Tour.Portal>
</Tour.Root>Examples
Centered
A welcome step with a null target, centered with no Arrow and no hole, then steps anchored to the page.
Controlled
open and step held by the page, with its own list of the steps and a Restart button that calls actionsRef; modal is off.
Late Target
The second step's chart renders only after the report loads: the first step stays shown, aria-busy and data-resolving, until it appears.
API reference
45 props across 15 parts, in React syntax. className, style, render are listed once, under Shared props.
ArrowTour.Arrow
Displays an element positioned against the target. Not rendered for a centered step. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the tour is open. |
data-closed | Present when the tour is closed. |
data-side | Indicates which side the popup is positioned relative to the target. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-uncentered | Present when the arrow is uncentered. |
BackdropTour.Backdrop
The dim layer, with the hole cut out of it. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the tour is open. |
data-closed | Present when the tour is closed. |
data-starting-style | Present when the backdrop is animating in. |
data-ending-style | Present when the backdrop is animating out. |
data-centered | Present when the shown step is centered: it has no target, or a zero-size or hidden one. |
| CSS variable | Description |
|---|---|
--spotlight-x | The x coordinate of the hole, in px. |
--spotlight-y | The y coordinate of the hole, in px. |
--spotlight-width | The width of the hole, in px. |
--spotlight-height | The height of the hole, in px. |
--spotlight-radius | The four corner radii of the hole, as a border-radius value. |
CloseTour.Close
A button that closes the tour. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
DescriptionTour.Description
A paragraph that describes the tour's popup and the step's target. Renders a <p>.
NextTour.Next
A button that shows the next step. On the last step it finishes the tour. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-last | Present on the last step, where the button finishes the tour. |
PopupTour.Popup
A container for the step's contents. It is focused on open and on every step change. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus on open and on every step change. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType, info: { step: TourStep<any> | null; index: number; }) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the tour closes. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the tour is open. |
data-closed | Present when the tour is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
data-side | Indicates which side of the target the popup is positioned on. Absent on a centered step. |
data-align | Indicates how the popup is aligned relative to the specified side. Absent on a centered step. |
data-instant | Present if animations should be instant. |
data-first | Present on the first step. |
data-last | Present on the last step. |
data-centered | Present when the shown step is centered. |
data-direction | The direction of the last step change. Absent on open. |
data-resolving | Present while the next step's target is being waited for. |
| CSS variable | Description |
|---|---|
--popup-width | The width of the popup. |
--popup-height | The height of the popup. |
PortalTour.Portal
A portal element that moves the tour's layers and popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the tour is closed. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
PositionerTour.Positioner
Positions the popup against the shown step's target, or in the middle of the viewport for a centered step. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
alignHow to align the popup relative to the specified side. | Align | 'center' |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
sideOffsetDistance between the target and the popup in pixels. | number | OffsetFunction | 12 |
| Data attribute | Description |
|---|---|
data-open | Present when the tour is open. |
data-closed | Present when the tour is closed. |
data-anchor-hidden | Present when the target is hidden. |
data-side | Indicates which side of the target the popup is positioned on. Absent on a centered step. |
data-align | Indicates how the popup is aligned relative to the specified side. Absent on a centered step. |
data-centered | Present when the shown step is centered. |
data-instant | Present when the last move came from scroll, resize or layout rather than a step change. |
| CSS variable | Description |
|---|---|
--available-width | The available width between the target and the edge of the viewport. |
--available-height | The available height between the target and the edge of the viewport. |
--anchor-width | The anchor's width. |
--anchor-height | The anchor's height. |
--transform-origin | The coordinates that this element is anchored to. Used for animations and transitions. |
--positioner-width | The width of the tour's positioner.
It is important to set width to this value when using CSS to animate size changes. |
--positioner-height | The height of the tour's positioner.
It is important to set height to this value when using CSS to animate size changes. |
PreviousTour.Previous
A button that shows the previous step. On the first step it is aria-disabled and stays focusable. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
focusableWhenDisabledWhether the button stays focusable on the first step, where it is disabled. | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present on the first step, where the button does nothing. |
ProgressTour.Progress
The shown step's position, "2 of 5" by default. Renders a <span>.
| Data attribute | Description |
|---|---|
data-first | Present on the first step. |
data-last | Present on the last step. |
RootTour.Root
Groups all parts of the tour. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
stepsrequiredThe steps of the tour, in order. | readonly TourStep<Data>[] | — |
defaultOpenWhether the tour is initially open. | boolean | false |
openWhether the tour is currently open. | boolean | — |
onOpenChangeEvent handler called when the tour is opened or closed. | ((open: boolean, eventDetails: TourRootChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the tour is opened or closed. | ((open: boolean) => void) | — |
stepThe id of the shown step. | string | — |
defaultStepThe id of the step an uncontrolled tour starts at. | string | — |
onStepChangeEvent handler called when a step is requested, before its target is looked up. | ((stepId: string, eventDetails: TourRootStepChangeEventDetails) => void) | — |
modalDetermines if the tour enters a modal state when open. | boolean | "trap-focus" | true |
disablePointerDismissalWhether a press outside the popup, the target and the spotlight element is ignored. | boolean | true |
disableTargetInteractionWhether pointer interactions with the target through the hole are blocked. | boolean | false |
keyboardNavigationWhether ArrowRight and ArrowLeft move to the next and previous steps. | boolean | true |
describeTargetWhether the shown target's aria-describedby gets the Description's id. | boolean | true |
spotlightPaddingThe space around the spotlight element in the hole, in px. | number | 8 |
spotlightRadiusThe corner radius of the hole: a number, the corners of an object, or 'auto' to follow the | TourSpotlightRadius | 'auto' |
targetTimeoutHow long to wait for a step's target to appear, in ms. | number | 3000 |
missingTargetWhat a step whose target is still missing at the timeout does: 'skip' shows the next step in | TourMissingTarget | 'skip' |
labelsThe texts of the parts. | Partial<TourLabels> | — |
actionsRefA ref to imperative actions. | React.RefObject<TourRootActions | null> | — |
handleA handle to associate the tour with detached triggers. | TourHandle | — |
childrenThe content of the tour. | React.ReactNode | ((args: TourChildRenderArgs<Data>) => React.ReactNode) | — |
SkipTour.Skip
A button that ends the tour for good, as opposed to closing it for now. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
SpotlightTour.Spotlight
A box at the hole, for a ring around the target. Not rendered while the step has no hole. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the tour is open. |
data-closed | Present when the tour is closed. |
data-starting-style | Present when the spotlight is animating in. |
data-ending-style | Present when the spotlight is animating out. |
data-instant | Present when the last move came from scroll, resize or layout rather than a step change. |
| CSS variable | Description |
|---|---|
--spotlight-x | The x coordinate of the hole, in px. |
--spotlight-y | The y coordinate of the hole, in px. |
--spotlight-width | The width of the hole, in px. |
--spotlight-height | The height of the hole, in px. |
--spotlight-radius | The four corner radii of the hole, as a border-radius value. |
TitleTour.Title
A heading that names the tour's popup. Renders a <h2>.
TriggerTour.Trigger
A button that starts the tour. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
stepThe id of the step the tour starts at when this trigger opens it. | string | — |
handleA handle to associate the trigger with a tour rendered elsewhere. | TourHandle | — |
disabledWhether the trigger ignores user interaction. | boolean | false |
idID of the trigger. | string | — |
| Data attribute | Description |
|---|---|
data-popup-open | Present while the tour is running. |
data-disabled | Present when the trigger is disabled. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| ArrowRightArrowLeft | Moves to the next or previous step, from the Popup or the target | Popup |
| ArrowRightArrowLeft | Swapped in right-to-left text | Popup |
| ArrowRightArrowLeft | Does nothing at the first and the last step | Popup |
| Escape | Closes the tour, also while a target is looked for | Popup |
| Tab | Moves from the Popup to the target and back, while modal | Popup |
| Tab | With modal off, leaves the Popup for what follows the target | Popup |
| EnterSpace | Opens the tour, at its first step or at the Trigger's step | Trigger |
| EnterSpace | Moves to the next step; on the last step reads "Done" and closes | Next |
| EnterSpace | Moves to the previous step; on the first step it is disabled and focusable | Previous |
| EnterSpace | Ends the tour, with the reason skip-press or close-press | Skip |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="dialog", and no aria-expanded | a reader hears that a dialog opens, and nothing claims the button controls the page |
| Popup | role="dialog", tabindex="-1", named by the Title (or "Step 2 of 3" without one), described by the Description; focused on each step | each step is announced when it is shown, its title first |
| Popup | aria-busy="true" while the target is looked for | a reader is not sent to a step whose element is not there yet |
| Description | its id added to the target's aria-describedby, beside the target's own, and removed on the next step | the target, once reached with Tab, is read with what the step says about it |
| Backdrop | aria-hidden="true", role="presentation" | the dim layer is never read |
| Spotlight | aria-hidden="true", pointer-events: none | the ring around the target is decoration only |
| Arrow | aria-hidden="true"; not rendered for a centered step | the arrow is decoration, and a centered step points at nothing |
| Progress | text only, "2 of 3", no role | the count is read with the Popup, not announced on every change |
| Previous | aria-disabled on the first step, still focusable | the button keeps its place in the Tab order and says why it does nothing |
| Root | with modal, every element outside the Popup, the target and the spotlight element hidden from assistive technology, restored on close | a reader stays in the tour and on the element it explains |
Focus order
- The Popup itself, focused when the tour opens and after each step change.
- Then its controls in order: Close, Skip, Previous and Next in the examples.
- Then, with
modal, the target and anything focusable in it, and back to the Popup; withmodaloff, Tab goes on to what follows the target.