Public API
Specify the small consumer-facing contract for tours and targets.
Public API
This page defines the implemented version 1 contract.
Tour definition
export default defineTour({
id: 'getting-started',
steps: [
{
id: 'welcome',
ariaLabel: 'Welcome',
content: 'Welcome to the application.',
},
{
id: 'projects',
route: { name: 'projects' },
target: 'new-project',
title: 'Create a project',
content: 'Start your first project here.',
placement: 'bottom-start',
prepare: async ({ signal }) => {
const panel = useProjectPanel()
panel.open()
await nextTick()
if (signal.aborted) return
return () => panel.close()
},
},
],
})defineTour() preserves literal tour and step IDs. Every definition has at
least one step. Tour IDs and step IDs are unique.
Step contract
interface TourStep {
readonly id: string
readonly target?: TourTarget
readonly scrollTarget?: TourTarget
readonly route?: TourRoute
readonly title?: string
readonly ariaLabel?: string
readonly content: string | Component
readonly placement?: TourPlacement
readonly offset?: number
readonly scroll?: false | Readonly<ScrollIntoViewOptions>
readonly interaction?: 'modal' | 'target' | 'page'
readonly when?: (context: TourStepContext) => boolean | Promise<boolean>
readonly prepare?: (
context: TourStepContext,
) => void | (() => void) | Promise<void | (() => void)>
}An omitted target means an intentionally centered step. A requested target
that does not exist never falls back to the center.
Targeted steps scroll the target to the vertical center by default and preserve
its nearest horizontal position. Use scroll: false to keep the current viewport,
or pass ScrollIntoViewOptions when a product needs different behavior. Use
scrollTarget to center a stable outer section while spotlighting a more precise
target inside it. This keeps nearby steps steady and still restores that section
when a user moves back from a distant step.
interaction: 'target' requires a target. The other modes also work for
centered steps.
Named-route params and every route's query fields accept serializable strings,
finite numbers, null, and arrays of those values. Runtime objects and functions
do not belong in definitions; perform that work in prepare() instead.
A visible title names the dialog. A step without a title requires an
ariaLabel. Runtime validation rejects empty names.
Target contract
type TourTarget =
| string
| {
id: string
timeout?: number
missing?: 'skip' | 'error'
}
| {
selector: string
timeout?: number
missing?: 'skip' | 'error'
}A string is a semantic target ID. The engine resolves it from the current Vue application's target registry.
<button v-tour-target="'new-project'">New project</button>Dynamic Vue elements register the same semantic ID:
const trigger = useTemplateRef<HTMLElement>('trigger')
useTourTarget('new-project', trigger)Direct refs do not belong in TourStep.target. The registry connects local Vue
state to stable tour definitions. Selector objects are an explicit escape hatch
for third-party or legacy markup.
Controller
const tour = useNuxtTour('getting-started')
tour.isActive
tour.currentStep
tour.currentStepId
tour.index
tour.total
tour.pending
await tour.start()
await tour.start({ at: 'projects', replace: true })
await tour.next()
await tour.previous()
await tour.goTo('projects')
await tour.skip()
await tour.cancel('application-state-changed')
const stop = tour.on('step:show', event => {
console.log(event.stepId)
})useNuxtTour() is the Nuxt control plane. Plain Vue uses useTour(definition).
Components do not expose a
second controller through template refs. Navigation uses stable step IDs, not
numeric indexes.
index is zero-based and refers to the definition order. total includes
conditional steps, including steps skipped at runtime. This avoids evaluating
all conditions early only to calculate progress.
next() and previous() skip unavailable conditional steps. goTo(id) and
start({ at: id }) are exact and reject with STEP_UNAVAILABLE when their
named destination is not currently eligible.
Controller state fields are readonly Vue refs. Templates unwrap them; scripts
read .value.
Custom card
TourHost always owns the dialog wrapper and accessibility lifecycle. The
#card slot receives step, controller, index, total, titleId,
descriptionId, and pending. Put the visible title and description on those
IDs when you render custom card content.
Exports
The package root exports the Nuxt module, ModuleOptions, TourError, and its
error types. The /vue entry exports:
defineTour
useTour
useTourTarget
TourHost
TourContent
createTourPlugin
TourError
TourErrorCode
public TypeScript typesThe CSS entries are @lupinum/nuxt-tour/style.css and
@lupinum/nuxt-tour/structure.css.
The default theme follows prefers-color-scheme. Class-based applications can
set .light or .dark; attribute-based applications can set
data-theme="light" or data-theme="dark". Application CSS can override the
low-specificity --tour-* custom properties without !important.
The stable theme variables are --tour-card-background, --tour-card-color,
--tour-card-radius, --tour-card-shadow, --tour-overlay-color,
--tour-spotlight-padding, --tour-spotlight-radius, --tour-accent,
--tour-accent-hover, --tour-accent-contrast, --tour-muted,
--tour-border, --tour-hover, and --tour-focus.
In a plain Vue application, pass a definition to useTour() for complete tour
and step-ID inference. Nuxt generates application-specific declarations for the
strict useNuxtTour() composable. An unknown Nuxt tour or step ID fails during
type checking. Semantic IDs used by useTourTarget() are inferred from those
same definitions.