Vize

Tour

Accessible, unstyled onboarding tour with positioned step content and a target spotlight.

Package @vizejs/ui/tour
Maturity stable
Own the source vize lib pull tour
Requires context, controllable-state, dismissable-layer, focus-scope, id, portal, positioner, presence
Aliases tour, product tour, onboarding, walkthrough, coachmark, guided tour
Covers WAI-ARIA Dialog (non-modal), Ark UI Tour, Shepherd.js, Driver.js, Reactour

Usage

import { Tour, TourRoot, TourArrow, TourClose, TourContent, TourDescription, TourNext, TourPrev, TourProgress, TourSpotlight, TourStep, TourTitle } from "@vizejs/ui/tour";

Or copy the source into your project with vize lib pull tour (see Source Distribution).

API

Tour<TStep extends TourStepDefinition>

Source: src/families/overlays/tour/tour-root.vue

Props

Prop Type Default Description
steps (required) readonly TStep[] required Ordered step definitions. The step type, including consumer-owned fields, is inferred and returned through slot state and v-model:step.
id string | null undefined Consumer-owned Tour base id. null and undefined select a deterministic fallback.
open boolean undefined Controlled open state. undefined selects uncontrolled behavior.
defaultOpen boolean false Initial open state for uncontrolled use.
step StepValue | null undefined Controlled current step value. undefined selects uncontrolled behavior.
defaultStep StepValue undefined Initial step for uncontrolled use. undefined selects the first enabled step.
placement Placement "bottom" Placement used by steps that do not declare their own.
missingTarget TourMissingTargetBehavior "center" Behavior when a declared step target is not found on the client. skip continues in the navigation direction; center keeps the step and centers the content.
scrollIntoView boolean true Scroll each resolved target into view. Motion is instant under prefers-reduced-motion.
markTarget boolean true Mark the current target with data-vize-tour-target="active" for consumer highlighting.
keyboardNavigation boolean true Let ArrowLeft and ArrowRight inside TourContent move between steps.
dir TourDirection "ltr" Reading direction used to map horizontal arrow keys.
beforeEnter TourBeforeEnter<TStep> undefined Hook run before any step becomes current, before the step's own beforeEnter. Receives the inferred step type. Resolving false cancels; rejecting emits navigation-error. While it is pending the root publishes data-pending and TourPrev/TourNext are disabled; a newer request aborts the pending one through signal.
afterLeave TourAfterLeave<TStep> undefined Hook run after a step stops being current, including when the tour closes.
messages Partial<TourMessages> undefined Overrides for default strings rendered by Tour parts.

Events

Event Payload Description
update:open [value: boolean] Fired when the tour requests a controlled open value.
open-change [value: boolean, previous: boolean, nativeEvent: Event | null] Fired after any distinct open-state request.
update:step [value: StepValue] Fired when the tour requests a controlled step value.
step-change [value: StepValue, previous: StepValue | null, nativeEvent: Event | null] Fired after any distinct step request.
complete [nativeEvent: Event | null] Fired when TourNext is activated on the last step or complete() is called.
dismiss [reason: TourDismissReason, nativeEvent: Event | null] Fired when an open tour closes without completing.
navigation-cancel [value: StepValue, from: StepValue | null] Fired when a beforeEnter hook resolves false; the current step is kept.
navigation-error [error: unknown, value: StepValue, from: StepValue | null] Fired when a beforeEnter hook throws or rejects; the current step is kept.

Slots

Slot Slot props Description
default TourSlotState<TStep> Compound Tour children. Receives the inferred current step and progress.

Exposed

Member Type Description
complete —
dismiss —
first —
goTo —
id —
index —
last —
next —
open —
pending —
previous —
refresh —
setOpen —
start —
state —
step —
target —
targetState —
total —
value —

TourRoot<TStep extends TourStepDefinition>

Source: src/families/overlays/tour/tour-root.vue

Props

Prop Type Default Description
steps (required) readonly TStep[] required Ordered step definitions. The step type, including consumer-owned fields, is inferred and returned through slot state and v-model:step.
id string | null undefined Consumer-owned Tour base id. null and undefined select a deterministic fallback.
open boolean undefined Controlled open state. undefined selects uncontrolled behavior.
defaultOpen boolean false Initial open state for uncontrolled use.
step StepValue | null undefined Controlled current step value. undefined selects uncontrolled behavior.
defaultStep StepValue undefined Initial step for uncontrolled use. undefined selects the first enabled step.
placement Placement "bottom" Placement used by steps that do not declare their own.
missingTarget TourMissingTargetBehavior "center" Behavior when a declared step target is not found on the client. skip continues in the navigation direction; center keeps the step and centers the content.
scrollIntoView boolean true Scroll each resolved target into view. Motion is instant under prefers-reduced-motion.
markTarget boolean true Mark the current target with data-vize-tour-target="active" for consumer highlighting.
keyboardNavigation boolean true Let ArrowLeft and ArrowRight inside TourContent move between steps.
dir TourDirection "ltr" Reading direction used to map horizontal arrow keys.
beforeEnter TourBeforeEnter<TStep> undefined Hook run before any step becomes current, before the step's own beforeEnter. Receives the inferred step type. Resolving false cancels; rejecting emits navigation-error. While it is pending the root publishes data-pending and TourPrev/TourNext are disabled; a newer request aborts the pending one through signal.
afterLeave TourAfterLeave<TStep> undefined Hook run after a step stops being current, including when the tour closes.
messages Partial<TourMessages> undefined Overrides for default strings rendered by Tour parts.

Events

Event Payload Description
update:open [value: boolean] Fired when the tour requests a controlled open value.
open-change [value: boolean, previous: boolean, nativeEvent: Event | null] Fired after any distinct open-state request.
update:step [value: StepValue] Fired when the tour requests a controlled step value.
step-change [value: StepValue, previous: StepValue | null, nativeEvent: Event | null] Fired after any distinct step request.
complete [nativeEvent: Event | null] Fired when TourNext is activated on the last step or complete() is called.
dismiss [reason: TourDismissReason, nativeEvent: Event | null] Fired when an open tour closes without completing.
navigation-cancel [value: StepValue, from: StepValue | null] Fired when a beforeEnter hook resolves false; the current step is kept.
navigation-error [error: unknown, value: StepValue, from: StepValue | null] Fired when a beforeEnter hook throws or rejects; the current step is kept.

Slots

Slot Slot props Description
default TourSlotState<TStep> Compound Tour children. Receives the inferred current step and progress.

Exposed

Member Type Description
open boolean Whether the tour is open.
state TourState Stable open state token.
step Step | null Current step definition, or null when no enabled step exists.
value TourStepValue<Step> | null Current step value, or null when no enabled step exists.
index number Zero-based position of the current step among enabled steps, or -1.
total number Number of enabled steps.
first boolean Whether the current step is the first enabled step.
last boolean Whether the current step is the last enabled step.
targetState TourTargetState Resolution status of the current step target.
pending boolean Whether a beforeEnter hook is pending.
id string Root-owned base id for the Tour family.
target Element | null Currently resolved target element, if any.
start (event?: Event | null) => boolean Open the tour at the first enabled step. Reports whether a step could be entered.
setOpen (value: boolean, event?: Event | null) => boolean Request an open state. Opening resumes at the current step.
next (event?: Event | null) => boolean Move to the next enterable step, or complete the tour on the last step.
previous (event?: Event | null) => boolean Move to the previous enterable step.
goTo (value: TourStepValue<Step>, event?: Event | null) => boolean Move to an enabled step by value, regardless of target availability.
dismiss (reason?: TourDismissReason, event?: Event | null) => boolean Close the tour with a dismissal reason.
complete (event?: Event | null) => boolean Close the tour as completed.
refresh () => void Re-resolve the current target, for targets rendered after the step became active.

TourArrow

Source: src/families/overlays/tour/tour-arrow.vue

Slots

Slot Slot props Description
default TourArrowSlotState Decorative arrow contents. Receives coordinates for optional custom drawing.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.
x —
y —

TourClose

Source: src/families/overlays/tour/tour-close.vue

Props

Prop Type Default Description
disabled boolean false Disable this control.
ariaLabel string undefined Accessible name when the visible label is not enough.
reason TourCloseReason "close" Dismissal reason reported by TourRoot. Use skip for "Skip tour" controls.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before dismissing the tour. Call preventDefault() to keep the tour unchanged.

Slots

Slot Slot props Description
default TourControlSlotState Control label. Receives position and availability state; defaults to the TourRoot messages label.

TourContent

Source: src/families/overlays/tour/tour-content.vue

Props

Prop Type Default Description
forceMount boolean false Keep the content mounted while the tour is closed.
to string | HTMLElement "body" CSS selector or element the tour layer is moved into.
portalDisabled boolean false Render in place instead of teleporting.
defer boolean true Keep content in place until the portal target exists, avoiding SSR mismatch.
strategy PositionerStrategy "fixed" CSS positioning mode published on the floating host.
offset number 12 Gap on the main axis between the target and the content.
collisionPadding number 8 Viewport padding the content should not cross.
arrowPadding number 0 Inset kept between the arrow and content edges.
flip boolean true Flip to the opposite side when the preferred side overflows more.
shift boolean true Shift the content back into the viewport after flip.
size boolean false Constrain the host and publish positioner available-size CSS variables.
safeArea boolean false Keep content clear of safe-area insets.
hide boolean false Mark the host hidden when the target leaves the viewport.
updateOnScroll boolean true Recalculate while ancestors scroll.
updateOnResize boolean true Recalculate when the document or visual viewport resizes.
viewport Rect undefined Viewport used for flip, shift, and hide.
modal boolean false Publish aria-modal="true" and contain focus inside the content.
autoFocus boolean true Move focus to the content when the tour opens.
restoreFocus boolean true Restore focus to the previously focused element when the tour closes.
focusOnStepChange boolean true Move focus to the content whenever the current step changes.
closeOnEscape boolean true Let Escape dismiss the tour with reason escape-key.
closeOnPointerDownOutside boolean false Let pointer-down outside the content and target dismiss the tour.
closeOnFocusOutside boolean false Let focus moving outside the content and target dismiss the tour.
ariaLabel string undefined Accessible name when no TourTitle labels the content.
ariaLabelledby string | null undefined Ids that label the content. null omits the default TourTitle id.
ariaDescribedby string | null undefined Ids that describe the content. null omits the default TourDescription id.

Events

Event Payload Description
open-auto-focus [event: FocusScopeAutoFocusEvent] Fired before automatic entry focus is applied.
close-auto-focus [event: FocusScopeAutoFocusEvent] Fired before automatic focus restoration is applied.
escape-key-down [event: DismissableLayerEscapeKeyDownEvent] Fired before Escape requests dismissal.
pointer-down-outside [event: DismissableLayerPointerDownOutsideEvent] Fired before an outside pointer-down requests dismissal.
focus-outside [event: DismissableLayerFocusOutsideEvent] Fired before outside focus movement requests dismissal.
interact-outside [event: DismissableLayerInteractOutsideEvent] Fired before outside pointer or focus interaction requests dismissal.
dismiss [event: DismissableLayerDismissEvent] Fired after an unprevented dismissal request, before the tour closes.

Slots

Slot Slot props Description
default TourContentSlotState Step content. Receives the current step, progress, and resolved placement.

Exposed

Member Type Description
element HTMLDivElement | null Rendered dialog element while mounted.
state TourState Stable open state token.
placement TourContentPlacement Resolved placement, or center.
focusContent (options?: FocusOptions) => void Move focus to the dialog element.
update () => void Recompute the floating position against the current target.

TourDescription

Source: src/families/overlays/tour/tour-description.vue

Slots

Slot Slot props Description
default TourSlotState Description text. Receives the current step and progress.

TourNext

Source: src/families/overlays/tour/tour-next.vue

Props

Prop Type Default Description
disabled boolean false Disable this control.
ariaLabel string undefined Accessible name when the visible label is not enough.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before moving to the next step or completing on the last step. Call preventDefault() to keep the tour unchanged.

Slots

Slot Slot props Description
default TourControlSlotState Control label. Receives position and availability state; defaults to the TourRoot messages label.

TourPrev

Source: src/families/overlays/tour/tour-prev.vue

Props

Prop Type Default Description
disabled boolean false Disable this control.
ariaLabel string undefined Accessible name when the visible label is not enough.

Events

Event Payload Description
click [nativeEvent: MouseEvent] Fired before moving to the previous step. Call preventDefault() to keep the tour unchanged.

Slots

Slot Slot props Description
default TourControlSlotState Control label. Receives position and availability state; defaults to the TourRoot messages label.

TourProgress

Source: src/families/overlays/tour/tour-progress.vue

Slots

Slot Slot props Description
default TourProgressSlotState Progress text. Defaults to TourRoot messages.progress(current, total) (current / total).

TourSpotlight

Source: src/families/overlays/tour/tour-spotlight.vue

Props

Prop Type Default Description
padding number 4 Pixels added around the target box on every side.
radius number 0 Corner radius published as --vize-ui-tour-spotlight-radius, in pixels.
interactive boolean false Whether the highlighted target stays interactive. Published as data-interactive so consumer CSS can cut a pointer hole; the spotlight never blocks input on its own.

Slots

Slot Slot props Description
default TourSpotlightSlotState Optional overlay contents, such as an SVG mask. Receives the padded target box.

Exposed

Member Type Description
element HTMLDivElement | null Rendered overlay element while open.
rect TourSpotlightRect | null Padded target box, or null.
update () => void Re-measure the target box.

TourStep

Source: src/families/overlays/tour/tour-step.vue

Props

Prop Type Default Description
value (required) string required Step value whose content this part renders.

Slots

Slot Slot props Description
default TourStepSlotState Content rendered only while this step is current; the hidden wrapper stays mounted.

TourTitle

Source: src/families/overlays/tour/tour-title.vue

Props

Prop Type Default Description
as "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" "h2" Heading element rendered for the step title.

Slots

Slot Slot props Description
default TourSlotState Title text. Receives the current step and progress.

Behavior

Normative state x input -> outcome table for tour-root.vue, tour-content.vue, tour-title.vue, tour-description.vue, tour-step.vue, tour-progress.vue, tour-prev.vue, tour-next.vue, tour-close.vue, tour-arrow.vue, and tour-spotlight.vue (@vizejs/ui/tour). Every row is proven by the named test.

ID State Input Outcome Evidence
U1 closed render, then start() nothing but the root and hidden hosts render; starting opens a non-modal role="dialog" labelled and described by TourTitle/TourDescription ids stays closed until started, then wires an accessible non-modal dialog
U2 open, targetless step render content is centered (data-placement="center", empty positioner style), the arrow is hidden, progress reads 1 / 3, and TourPrev is disabled stays closed until started, then wires an accessible non-modal dialog
U3 open TourNext / TourPrev steps advance and retreat; resolved targets anchor the content, receive data-vize-tour-target="active", and scroll into view next and previous walk steps, anchor resolved targets, and complete on the last step
U4 open, last step TourNext TourNext publishes data-last; activation closes the tour, emits complete, and unmarks the target next and previous walk steps, anchor resolved targets, and complete on the last step
U5 open step change focus moves to the dialog so the new step is announced next and previous walk steps, anchor resolved targets, and complete on the last step
U6 opened from a launcher close focus returns to the element focused before the tour opened restores focus to the launcher when the tour closes
U7 controlled open / step navigation or close requests are emitted, rendered state stays controlled until the parent accepts controlled open and step emit requests without changing until the parent accepts
U8 declared target not found enter step, missingTarget="center" the step stays current with data-target="missing" and centered content; invalid selectors resolve as missing missing targets center by default and skip in the navigation direction when configured
U9 declared target not found enter step, missingTarget="skip" navigation continues in its direction; per-step missingTarget overrides the root and applies to the initial step after mount missing targets center by default and skip in the navigation direction when configured
U10 disabled step navigation / goTo disabled steps leave the sequence, progress totals, and goTo rejects them disabled steps leave the sequence and progress counts
U11 open ArrowRight / ArrowLeft / Escape arrows navigate (mirrored for dir="rtl") except inside editable controls; Escape dismisses with escape-key unless closeOnEscape is false Escape dismisses with a reason and arrow keys navigate outside editable controls
U12 open TourClose click preventDefault() in the click handler keeps the tour open; otherwise it dismisses with the control's reason (default close) close controls report their reason and preventDefault keeps the tour open
U13 open TourClose reason="skip" dismisses with skip and never emits complete skip controls dismiss with the skip reason
U14 open, resolved target render, resize, targetless step TourSpotlight publishes padded --vize-ui-tour-target-{x,y,width,height} and radius, re-measures on resize, and clears them without a target spotlight publishes padded target geometry as custom properties
U15 exposed instance setOpen, goTo, refresh, complete, dismiss typed imperative controls work; refresh() resolves targets rendered after the step became current exposes typed imperative controls and re-resolves late targets on refresh
U16 reduced motion / opt-outs enter step scrolling is instant under prefers-reduced-motion; scrollIntoView=false and markTarget=false leave the target untouched respects reduced motion when scrolling targets into view
U17 missing provider setup compound parts fail closed with the shared context diagnostic compound parts require a matching root provider
U18 SSR two isolated renders markup is byte-identical, targets report pending, content is centered, and no geometry is emitted renders byte-identical open tour markup across isolated SSR requests
U19 SSR, closed render the content host is hidden and no dialog is emitted renders a closed tour without dialog content
U20 hydration mount over server HTML server nodes are kept without diagnostics; targets resolve after mount hydrates the open tour without replacement or diagnostics, then resolves targets
U21 pure helpers selectors, refs, getters, rects target resolution never throws, step walks never wrap, and spotlight boxes clamp at zero resolves selector, element, ref, and getter targets without throwing, walks enabled steps in one direction without wrapping, pads spotlight boxes and clamps negative sizes, maps placements, including the centered fallback, to side and alignment tokens, detects editable keyboard targets
U22 open, async beforeEnter TourNext the request stays pending (data-pending, pending, TourPrev/TourNext disabled); on true the step commits and its target is re-resolved async beforeEnter keeps navigation pending, disables controls, and re-resolves the target
U23 open, beforeEnter hook returns/resolves false, throws, or rejects the step is kept; navigation-cancel(value, from) or navigation-error(error, value, from) is emitted and no step request is made beforeEnter false cancels and failures report navigation-error while keeping the step
U24 pending beforeEnter newer navigation / dismissal the stale hook's signal aborts and its settlement is ignored; the latest request wins a newer navigation aborts the pending hook and the latest request wins
U25 open step change / close afterLeave({ step, to }) runs after the step stops being current, with to: null on close afterLeave runs after steps change and when the tour closes
U26 messages render TourProgress uses messages.progress(current, total); empty TourPrev/TourNext/TourClose slots fall back to the configured labels messages override progress and fallback control labels

Notes

  • beforeEnter hooks (TourRoot prop first, then the step's own) run for navigation requests (start, setOpen(true), next/previous, goTo, missing-target skips), never while rendering; the initial defaultOpen/controlled step does not run them. Synchronous hooks settle in the same call. Steps with hooks are always enterable so a hook may render a skip target first.

  • Steps are data (steps prop). TourRoot is a generic SFC: the step type, including consumer-owned fields such as title, is inferred from steps and returned through the root slot, v-model:step, and the exposed instance. TourStep renders per-step template content only while its value is current.

  • Target resolution, marking, scrolling, and measurement run only on the client after mount; SSR and the hydration pass report data-target="pending" and a centered placement.

  • TourContent reuses the Positioner controller (collision-aware placement against the target), the dismissable layer (Escape and optional outside dismissal; the target is a branch), and the focus scope (entry focus on the dialog, optional containment when modal, focus restoration). Centered steps clear the positioner host style; consumer CSS centers [data-vize-ui="tour-positioner"][data-placement="center"].

  • TourSpotlight is decorative (aria-hidden="true") and never blocks input on its own; use the published custom properties (for example with clip-path or an SVG mask) to draw the highlight and data-interactive to decide pointer behavior.