AbsoluteJS

Tour

@absolutejs/tourv0.3.0-beta.6betaFrontend & UX

Element-level, cross-page product tour engine for Vue apps — a serializable step protocol, a pixel-accurate spotlight, and a controller that survives full-page navigations.

@absolutejs/tour is an element-level, cross-page product tour engine for Vue apps. Tours are described by a small serializable step protocol — target selector, route, placement, copy — so they can live in code, in a database, or be authored in an admin UI, and all render through the same engine. The spotlight stays pixel-accurate through late layout shifts, and a sessionStorage-backed controller resumes tours across full-page navigations in MPAs. Vue 3 and vue-router are peer dependencies; you render a tiny overlay component with your own styling.

#Installation

BASH
bun add @absolutejs/tour

#Capabilities

Overview

Element-level, cross-page product tour engine for AbsoluteJS apps.

A tour is described by a small, serializable protocol (steps with a target selector, route, placement, and copy) so it can live in code, in a database, or be authored in an admin UI — and rendered by the same engine. The engine gives you:

Spotlight positioning — dims the page, highlights one real element, and

Show 6 more

stays pixel-accurate. It re-measures on a light interval so the highlight follows late layout shifts (a page loading its data and reflowing after the first measure), not just scroll/resize.

Cross-page resume — in an MPA each page is its own mount, so moving between

steps is a full reload. The controller persists progress in sessionStorage and resumes on the next page at the right step.

A shared controller — start/stop the tour from anywhere (a first-visit

trigger, a "replay" button), all driving one overlay.

Vue 3 and vue-router are peer dependencies. The engine ships as composables; you render a tiny overlay component with your own styling (see below).

The protocol

Add data-tour="…" attributes to the elements you want to spotlight. A step with no target (or one that can't be found) renders as a centered card.

Wiring it up

Render an overlay component that consumes useSpotlight (style it however you like — the engine only computes geometry):

The host app owns: the data-tour anchors, the step content, and where the "seen" marker is stored (so finishing a first-visit run stamps it; a replay does not).

Step actions — demo the product, don't just point at it

Steps stay serializable, so a step references actions by name; the host registers the handlers. onEnter actions run once the step is positioned (sequentially, cancelled if the step changes mid-run); onExit actions run when the step is left — cleanup/restore.

Built-ins (no host code needed): click, scroll, wait — each takes an optional selector (default: the step's target). Unknown action names warn and skip so a tutorial authored against an unmounted page degrades instead of breaking the tour. Handlers receive { step, target, args, index, signal, next, back, stop } — a handler can drive the tour itself (e.g. auto-advance when its demo finishes).

Demo data — tour an empty account for free

A tour must be able to show a data-backed surface (matches, pipeline) to a viewer who has no data yet — a fresh signup, an unsubscribed user — without the host paying to source anything. useTourDemo swaps in a constant, fully-typed sample dataset while the tour plays and passes the real data through untouched otherwise:

Resolution is per TourDataMode: "auto" (default) shows the viewer's real data when they have it — so a member with sourced matches is toured on their literal matches — and the sample when they don't; "demo" / "live" force one side (Tutorial.dataMode carries the choice in the serialized tutorial). Badge the surface when isDemo is true so sample data is never mistaken for real.

Funnel events — see where viewers bail

Pass onEvent to useSpotlight and every lifecycle moment lands in your analytics: tour_started, step_viewed, step_completed, step_target_missing, action_failed, tour_completed, and — the one that matters — tour_skipped, carrying the exact stepIndex, stepTitle, target, and route (the screen the viewer was on when they'd had enough) plus a reason distinguishing the Skip button from Escape.

Branching & readiness — showIf / skipIf / waitFor

Conditions mirror actions: serializable refs resolved by name against a registry (useTourConditions), with element and media built in.

Skipped steps are hopped in the direction of travel; if everything ahead is skipped the tour completes cleanly.

Mobile variants

Below mobileQuery (default (max-width: 640px)) a step's mobile block overrides its target/placement/copy — or skips it where the anchor doesn't exist on small screens:

CTA buttons

cta: { label: "Try it now", actions: [{ action: "click" }] } renders a button in the card (host template: v-if="step.cta" → @click="runCta"); it runs the refs through the action registry and advances unless advance: false.

Auto-play gate — stop nagging people

useTourGate owns the auto-play decision across MANY tutorials: dismissal caps (trigger.maxDismissals, default 2 — after that, manual replay only), oncePerSession, priority when several tutorials match a page, audience showIf predicates, and role matching. State persists in localStorage.

Checklist — "getting started" engine

Typed tasks + completion persistence + progress math; the host renders the panel. completeForTutorial(slug) checks off tasks tied to a tutorial (wire it to the tour_completed event).

Hotspots — always-on help beacons

Persistent pulsing beacons on tricky UI (independent of any tour) that open an explainer card on click. once: true hides a beacon after it's been opened; dismissals persist.

Spotlight positioning

Dims the page and highlights one real element, re-measuring on a light interval so the highlight follows late layout shifts, not just scroll and resize.

Cross-page resume

The controller persists progress in sessionStorage, so a step that navigates to another page resumes at the right step after the full reload.

Serializable actions

Steps reference actions and conditions by name; hosts register handlers, with click, scroll, wait, element, and media built in — so a tour can demo the product, not just point at it.

Demo data mode

useTourDemo swaps in a fully-typed sample dataset while the tour plays, so fresh accounts can be toured on data-backed surfaces for free.

Funnel events

Every lifecycle moment — started, viewed, completed, skipped with the exact step and route — lands in your analytics via onEvent.

Auto-play gating

useTourGate handles dismissal caps, once-per-session, priority, and audience matching across many tutorials; checklist and hotspot engines round out onboarding.

Outcomes

What you can build

Overview

Element-level, cross-page product tour engine for AbsoluteJS apps.

The protocol

Add data-tour="…" attributes to the elements you want to spotlight. A step with no target (or one that can't be found) renders as a centered card.

Wiring it up

Render an overlay component that consumes useSpotlight (style it however you like — the engine only computes geometry):

Hardening checklist

Production guidance

Branching & readiness — showIf / skipIf / waitForConditions mirror actions: serializable refs resolved by name against a registry (useTourConditions), with element and media built in.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/tour example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#Branching & readiness — showIf / skipIf / waitFor

Partial snippet

Conditions mirror actions: serializable refs resolved by name against a registry (useTourConditions), with element and media built in.

TS
useTourConditions().register("hasDeals", () => dealCount.value > 0);

#Branching & readiness — showIf / skipIf / waitFor 2

Partial snippet

Conditions mirror actions: serializable refs resolved by name against a registry (useTourConditions), with element and media built in.

TS
{
	title: "Your pipeline",
	showIf: [{ condition: "hasDeals" }],           // all must hold, else skipped
	skipIf: [{ condition: "media", args: { query: "(max-width: 640px)" } }],
	waitFor: { selector: ".pipeline-board", timeoutMs: 5000 }, // hold until ready
}

#The protocol

Partial snippet

Working example for The protocol.

TS
import type { Tutorial } from "@absolutejs/tour";

const tour: Tutorial = {
  slug: "portal-intro",
  trigger: { firstVisitOnly: true, onRoutePrefix: "/portal" },
  steps: [
    {
      title: "Welcome",
      body: "A quick tour.",
      placement: "center",
      route: "/dashboard",
    },
    {
      title: "Your command center",
      body: "Your single best next step is always one click here.",
      route: "/dashboard",
      target: '[data-tour=\"hero\"]',
      placement: "bottom",
    },
    // …intake, matches, network — each navigates and spotlights a real element
  ],
};

#Wiring it up

Partial snippet

Working example for Wiring it up.

TS
// shared controller (singleton per storage key)
import { useTourController } from "@absolutejs/tour";
const tour = useTourController("myapp.tour");

// auto-play once on first visit, then stamp your own "seen" marker
if (!account.tourSeenAt) tour.start();

// replay from anywhere (does not re-stamp — pass replay=true)
tour.start(true);

#The Protocol

Partial snippet

Tours are plain serializable objects — safe to store in a database or author in an admin UI. Add data-tour attributes to the elements you want to spotlight; a step with no target renders as a centered card.

TS
import type { Tutorial } from '@absolutejs/tour';

const tour: Tutorial = {
	slug: 'portal-intro',
	steps: [
		{
			body: 'A quick tour.',
			placement: 'center',
			route: '/dashboard',
			title: 'Welcome'
		},
		{
			body: 'Your single best next step is always one click here.',
			placement: 'bottom',
			route: '/dashboard',
			target: "[data-tour='hero']",
			title: 'Your command center'
		}
	],
	trigger: { firstVisitOnly: true, onRoutePrefix: '/portal' }
};

#Wiring It Up

Partial snippet

Wire the shared controller and the spotlight engine; your overlay component consumes the computed styles and step state.

TS
import { useSpotlight, useTourController } from '@absolutejs/tour';
import { PORTAL_TOUR_STEPS } from './steps';

// Shared controller (singleton per storage key)
const controller = useTourController('myapp.tour');

// Render your overlay from the computed geometry and state
const t = useSpotlight({
	controller,
	onClose: () => emit('close'),
	onEvent: (event) => analytics.track(event),
	steps: () => PORTAL_TOUR_STEPS
});
// t.step, t.spotlightStyle, t.tooltipStyle, t.next, t.back, t.skip

// Auto-play once on first visit, then stamp your own "seen" marker
if (!account.tourSeenAt) {
	controller.start();
}

// Replay from anywhere (does not re-stamp)
controller.start(true);

#Step Actions

Partial snippet

Steps reference actions by name so they stay serializable; unknown action names warn and skip instead of breaking the tour.

TS
import { useTourActions } from '@absolutejs/tour';

// The page that OWNS the surface registers its demo handler
const actions = useTourActions();
const unregister = actions.register('matches.demo-swipe', async (ctx) => {
	const direction = ctx.args.direction === 'left' ? 'left' : 'right';
	await swiper.value?.demoSwipe(direction); // ctx.signal aborts long demos
});
onBeforeUnmount(unregister);

// The step invokes it — plain JSON, safe to store in a DB
const step = {
	onEnter: [
		{ action: 'matches.demo-swipe', args: { direction: 'right' } },
		{ action: 'wait', args: { ms: 700 } },
		{ action: 'matches.demo-swipe', args: { direction: 'left' } }
	],
	route: '/portal/matches',
	target: "[data-tour='match-view']",
	title: 'Swipe or list'
};
Beta
This package is in beta; the step protocol is designed to be stored, so breaking protocol changes are avoided, but composable APIs may still shift.
Headless by design
The engine only computes geometry and state — you own the overlay markup, the data-tour anchors, and where the "seen" marker is stored.

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

54 symbols
TourActionArgValueexportPermalink
TS
TourActionArgValue
Exported from @absolutejs/tour

Current package surface

What ships today

@absolutejs/tourv0.3.0-beta.6 · betaFrontend & UXnpmSource
3entry points55symbols

Import surface · click to copy