Skip to content

API Overview ​

SymbolPurposeExecution modeCommon gotcha
createTour(config)Runs a sequential, resumable walkthrough over the app's DOMSync (storage reads may be async)start() is a no-op when the stored record says the tour is completed
createCoachmark(config)Shows a single contextual hint anchored to one targetSync (storage reads may be async)show() is a no-op after dismiss() until reset() or a version bump
resolveTarget(target)Resolves any target to a viewport-space rect or nullSyncCall it per position update; a scrolled target's rect changes
BeaconErrorBase class for all beacon-originated errors—Catch this to catch any beacon error
BeaconConfigErrorInvalid tour/coachmark configurationThrown at creationEmpty steps or duplicate step ids fail at createTour()
BeaconDisposedErrorHandle used after dispose()Thrown on calldispose() is idempotent; later method calls throw

Package Entry Point ​

ImportPurpose
@vielzeug/beaconComplete public Beacon API

Core Functions ​

createTour ​

ts
function createTour(config: BeaconTourConfig): BeaconTourHandle

Returns a live handle for one guided tour. Throws BeaconConfigError when id is empty, steps is empty, or step ids are duplicated.

Parameters: see BeaconTourConfig.

Returns: a BeaconTourHandle that is also Disposable.

Example:

ts
import { createTour } from '@vielzeug/beacon';

const tour = createTour({
  id: 'release-tour',
  steps: [{ id: 'intro', title: 'What changed' }],
  autoStart: true,
});

tour.status.subscribe(() => console.log(tour.status.value));

Handle methods:

MemberBehavior
start()Begin or resume the tour. No-op when completed; throws after dispose.
next()Advance; past the last step, finishes the tour.
back()Return to the previous step. No-op on the first step.
goto(step)Jump to a step by index or id. Unknown values warn and no-op.
pause()Stop showing, keep progress so start() resumes it.
skip()End from a "Skip" control. Persisted as dismissed, reported with reason 'skip'.
finish()Mark finished and stop showing.
dismiss()End by closing (Escape, backdrop). Persisted as dismissed.
reset()Discard persisted progress; the tour is eligible again.
tap(handler, options?)Observe BeaconTourEvents; returns an unsubscribe, honors options.signal.
dispose() / [Symbol.dispose]()Release all listeners and observers. Idempotent.
Handle properties:
MemberTypeNotes
currentReadable<BeaconCurrentStep>The snapshot to render; step is null while closed. Beacon re-publishes the rect on scroll, resize, and target layout changes while a step is active.
statusReadable<BeaconStatus>Lifecycle, persisted on every change.
eligibleReadable<boolean>Whether the tour should be offered to this user.
stepsreadonly BeaconStep[]The configured steps, in order.
idstringThe configured id (also the storage key suffix).
disposedbooleanWhether dispose() has run.
disposalSignalAbortSignalAborted on dispose; pass it to your own listeners.

createCoachmark ​

ts
function createCoachmark(config: BeaconCoachmarkConfig): BeaconCoachmarkHandle

Returns a handle for one contextual hint. Throws BeaconConfigError when id is empty.

Parameters: see BeaconCoachmarkConfig.

Returns: a BeaconCoachmarkHandle that is also Disposable.

Example:

ts
import { createCoachmark } from '@vielzeug/beacon';

const hint = createCoachmark({
  id: 'share-hint',
  target: '[data-tour="share"]',
  title: 'Share this board',
});

hint.show(); // waits for the target to mount if it is not on screen yet

Handle members:

MemberBehavior
show()Show the hint. No-op once dismissed without a newer version.
hide()Hide without recording it as seen; it can show again.
dismiss()Hide and record as seen permanently (until reset()/version).
reset()Discard the persisted record; the hint is eligible again.
tap(handler, options?)Observe coachmark:show, coachmark:dismiss, and common events.
currentReadable<BeaconCurrentStep> — the hint as a single-step snapshot: step is the hint while visible, null while hidden. The rect stays live on scroll, resize, and target layout changes.
eligibleReadable<boolean> — never seen, or superseded by a newer version.

resolveTarget ​

ts
function resolveTarget(target: BeaconTarget): BeaconRect | null

Resolves a target to a viewport-space rect, or null when it is not available yet. Strings resolve through document.querySelector; functions are called and their result normalized.

ParameterTypeDescription
targetBeaconTargetSelector, Element, rect, or lazy resolver.

Returns: BeaconRect | null — shape-compatible with getBoundingClientRect() and @vielzeug/orbit's Rect.

Example:

ts
import { resolveTarget } from '@vielzeug/beacon';

const rect = resolveTarget('[data-tour="share"]');
// Anchor an orbit positioner from a virtual reference:
const reference = { getBoundingClientRect: () => resolveTarget('[data-tour="share"]') ?? ZERO_RECT };

Types ​

BeaconTourConfig ​

OptionTypeDefaultDescription
idstringrequiredUnique identity; used as the storage key.
stepsreadonly BeaconStep[]requiredEvery step, in order. At least one.
autoScrollbooleantrueScroll each anchored step's target into view when it activates.
autoStartbooleanfalseStart on the next microtask when eligible.
placementBeaconPlacement'auto'Default placement handed to the presentation layer.
presentationBeaconPresentation'auto'Default presentation handed to the presentation layer.
scrollOffsetnumber | (() => number)0Pixels kept clear at the viewport top for a sticky header, read at scroll time.
storageBeaconStoragememory-onlyProgress persistence.
targetTimeoutMsnumberInfinityHow long to wait for a step's target before skipping that step. 0 publishes immediately with a null rect.
versionnumber1Bump to re-show a finished tour.

BeaconCoachmarkConfig ​

OptionTypeDefaultDescription
idstringrequiredUnique identity; used as the storage key.
targetBeaconTargetrequiredWhere the hint points.
autoShowbooleantrueShow automatically when not yet seen.
dataunknown—Free-form data for the presentation layer.
descriptionstring—Supporting copy.
placementBeaconPlacement'auto'Preferred side.
presentationBeaconPresentation'auto'Preferred presentation.
storageBeaconStoragememory-onlySeen-record persistence.
titlestring—Headline copy.
versionnumber1Bump to re-show a seen coachmark.

BeaconStep ​

ts
interface BeaconStep {
  readonly data?: unknown;
  readonly description?: string;
  readonly id: string;
  readonly placement?: BeaconPlacement;
  readonly presentation?: BeaconPresentation;
  readonly target?: BeaconTarget;
  readonly title?: string;
}

One stop in a tour. id is stable and persisted — renaming it resets progress for that step. Omit target for a centered intro/outro step. placement/presentation override the tour-level defaults.

BeaconTarget ​

ts
type BeaconTarget = BeaconTargetResolver | BeaconRect | Element | string;

What a step points at. Selectors and elements anchor to real DOM; rects (or resolvers returning one) anchor to element-less geometry such as a canvas region.

BeaconTargetResolver ​

ts
type BeaconTargetResolver = () => Element | BeaconRect | null;

Resolves a target lazily; null means "not available yet".

BeaconRect ​

ts
interface BeaconRect {
  readonly height: number;
  readonly width: number;
  readonly x: number;
  readonly y: number;
}

A viewport-space rectangle in CSS pixels. Matches @vielzeug/orbit's Rect shape.

BeaconPlacement ​

ts
type BeaconPlacement =
  | 'auto'
  | 'bottom' | 'bottom-end' | 'bottom-start'
  | 'left' | 'left-end' | 'left-start'
  | 'right' | 'right-end' | 'right-start'
  | 'top' | 'top-end' | 'top-start';

Preferred side for an anchored presentation; 'auto' asks the positioning layer to choose.

BeaconPresentation ​

ts
type BeaconPresentation = 'auto' | 'bottom-sheet' | 'modal' | 'popover';

How a step should be presented. 'auto' lets the presentation layer pick by available space.

BeaconStatus ​

ts
type BeaconStatus = 'active' | 'completed' | 'dismissed' | 'idle' | 'paused';

Tour lifecycle, persisted across visits. 'idle' means never started.

BeaconProgress ​

ts
interface BeaconProgress {
  readonly status: BeaconStatus;
  readonly stepIndex: number;
  readonly version: number;
}

The persisted record for one tour or coachmark. stepIndex is -1 before the first step. Records are validated on read; invalid records fall back to a fresh tour.

BeaconStorage ​

ts
interface BeaconStorage {
  get(key: string): Promise<BeaconProgress | null | undefined> | BeaconProgress | null | undefined;
  remove(key: string): Promise<unknown> | unknown;
  set(key: string, value: BeaconProgress): Promise<unknown> | unknown;
}

Minimal storage contract; sync or async. Values are JSON. Keys arrive namespaced (@vielzeug/beacon:<id>). A @vielzeug/vault store plugs in with:

ts
const storage: BeaconStorage = {
  get: (key) => store.get('progress', key),
  remove: (key) => store.delete('progress', key),
  set: (key, value) => store.put('progress', { id: key, ...value }),
};

BeaconCurrentStep ​

ts
interface BeaconCurrentStep {
  readonly placement: BeaconPlacement;
  readonly presentation: BeaconPresentation;
  readonly rect: BeaconRect | null;
  readonly step: BeaconStep | null;
  readonly stepIndex: number;
  readonly totalSteps: number;
}

The snapshot a presentation layer renders from. step is null before start and after the tour ends; rect is null for centered steps and unresolved targets. While a step is active, Beacon keeps rect fresh on scroll, resize, and target layout changes — no app-side refresh call is needed.

BeaconTourEvent ​

ts
type BeaconTourEvent =
  | { readonly index: number; readonly step: BeaconStep; readonly type: 'step:start' }
  | { readonly index: number; readonly step: BeaconStep; readonly type: 'step:complete' }
  | { readonly reason: 'finish' | 'skip'; readonly type: 'tour:complete' }
  | { readonly type: 'tour:dismiss' }
  | { readonly status: BeaconStatus; readonly type: 'status-change' }
  | { readonly message: string; readonly type: 'target-missing' }
  | { readonly index: number; readonly message: string; readonly step: BeaconStep; readonly type: 'target-waiting' }
  | { readonly error: unknown; readonly message: string; readonly type: 'warning' }
  | { readonly type: 'dispose' };

Notable moments a tour reports through tap(). target-missing means a step was skipped because its target did not appear in time; target-waiting fires when a step begins waiting for its target to mount; warning carries storage or handler failures.

BeaconCoachmarkEvent ​

ts
type BeaconCoachmarkEvent =
  | { readonly type: 'coachmark:show' }
  | { readonly type: 'coachmark:dismiss' }
  | { readonly type: 'target-waiting' }
  | { readonly error: unknown; readonly message: string; readonly type: 'warning' }
  | { readonly type: 'dispose' };

Notable moments a coachmark reports through tap().

BeaconTourHandle / BeaconCoachmarkHandle ​

The objects returned by createTour() and createCoachmark(), documented in Core Functions. Both expose disposalSignal, disposed, tap(), dispose(), and [Symbol.dispose]().

Errors ​

ErrorTriggered byNotes
BeaconErrorBase for all beacon errorsname is the subclass name; cause supported via ErrorOptions.
BeaconConfigErrorEmpty id, empty steps, missing/duplicate step idsThrown synchronously from createTour()/createCoachmark().
BeaconDisposedErrorCalling handle methods after dispose()dispose() itself never throws.