Feature Guide

Everything SparkGuide can do, with a working example for each. If you haven't installed it yet, start with Getting Started.

🚢 Product tours (flows)

A flow is an ordered sequence of steps β€” the thing most people mean by "product tour." Register one with addFlow, then start it.

guide.addFlow('reports-tour', {
  steps: [
    { type: 'modal', title: 'New: Reports', content: 'Here’s what changed.' },
    { target: '#reports-table', content: 'Your saved reports now live here.' }
  ]
});

guide.start('reports-tour');                    // start (or resume) it
guide.start('reports-tour', { resume: false });  // always replay from step 0
guide.startMatching();                           // start every flow whose
                                                  // segment matches β€” great
                                                  // for a page-load hook
guide.dismiss('reports-tour');                   // close it early
guide.resetFlow('reports-tour');                 // clear its saved progress

startMatching() is the method most apps call once on page load: it walks every registered flow, checks its segment rule against the current user, and shows the first one that matches and hasn't already been seen.

Multiple matching flows never overlap. If more than one flow matches, startMatching() runs them one at a time β€” each is awaited until it's completed or dismissed before the next one starts, so you never get two tours' overlays stacked on top of each other.

πŸ–±οΈ Step types: tooltip, hotspot, modal

Each step in a flow renders one of three components. The type is inferred from the step's shape unless you set type explicitly:

Step looks likeRenders as
has a target, no typeTooltip β€” spotlight overlay + positioned card
type: 'hotspot'Hotspot β€” a pulsing dot; click it to reveal the content
type: 'modal', or no targetModal β€” centered dialog, not anchored to anything
{
  type: 'tooltip' | 'hotspot' | 'modal', // default: tooltip if `target` set, else modal
  target: '#css-selector',               // omit for modal steps
  title: 'string',
  content: 'string',
  placement: 'top' | 'bottom' | 'left' | 'right', // default 'bottom'
  advanceOn: 'click',                    // optional: auto-advance when the target is clicked
  showOverlay: true                      // tooltip-only: set false to skip the dimmed backdrop
}

Tooltips

Dim the rest of the page and cut out a "spotlight" around the target, with Back/Next (or Finish, on the last step) buttons.

Hotspots

Non-blocking β€” no dimmed backdrop, just a small pulsing indicator. Clicking it opens a popover with the content and, inside a flow, the same Back/Next navigation as a tooltip.

Modals

Centered dialogs, ideal for a flow's opening "Welcome" screen since they don't need to anchor to any element on the page.

SPA-safe. Targets are resolved asynchronously, so a step whose target never appears β€” or disappears mid-step, e.g. an SPA route change β€” is skipped automatically rather than getting the tour stuck.

πŸͺŸ Standalone modals

Not every dialog needs to be part of a multi-step flow:

guide.showModal({
  title: 'Heads up',
  content: 'Here’s something worth knowing.',
  primaryLabel: 'Got it',
  secondaryLabel: 'Remind me later',
  onPrimary: () => trackEvent('notice_ack'),
  onSecondary: () => scheduleReminder()
});

onPrimary/onSecondary/onClose are all optional β€” every control on the modal (the X, clicking outside, and both buttons) closes it regardless of whether you pass a callback.

πŸ“ Standalone hotspots

For a lightweight "psst, try this" nudge outside of any flow:

guide.showHotspot({
  target: '#new-feature-icon',   // a selector (waits for it to appear) or an Element
  title: 'New: Dark mode',
  content: 'Toggle it from here.',
  placement: 'right'
});

πŸ“£ Banners

A persistent top-of-page bar for announcements:

guide.showBanner({
  content: 'New: Roadmap & feature requests are here.',
  actionLabel: 'See what shipped',
  onAction: () => (window.location.href = '/changelog'),
  onClose: () => trackEvent('banner_dismissed')
});

Calling showBanner again replaces the current banner with a new one.

βœ… Checklists

A floating "getting started" checklist with a progress bar. Items can be checked off by the user, or completed programmatically from your own app code in response to a real action:

guide.addChecklist('getting-started', {
  title: 'Getting started',
  items: [
    { id: 'invite', label: 'Invite your team' },
    { id: 'project', label: 'Create your first project' },
    { id: 'integration', label: 'Connect an integration' }
  ]
});

// later, wherever the real action happens in your app:
createProjectButton.addEventListener('click', () => {
  guide.completeChecklistItem('getting-started', 'project');
});

The checklist minimizes to a small floating button and remembers which items are done across reloads. checklist:complete fires once, the moment the last item is checked β€” not again if an item is later unchecked and rechecked.

🎯 Segmentation & targeting

Any flow's segment option decides who sees it. All rules given are AND'd together:

guide.addFlow('admin-only-tour', {
  segment: {
    urlMatches: 'contains:/dashboard',   // or 'equals:/app', or a RegExp
    elementExists: '#admin-panel',       // only if this element is on the page
    user: { role: 'admin', plan: ['pro', 'enterprise'] } // value or array of allowed values
  },
  steps: [ /* ... */ ]
});

Tell SparkGuide who the current user is with identify() (merges into whatever you passed at construction time), and re-check segments any time:

guide.identify({ plan: 'pro' });   // e.g. right after an upgrade
guide.startMatching();             // re-evaluate and show anything newly unlocked

You can also call the matcher directly β€” handy for building your own "would this flow show right now?" debug panel:

import { Targeting } from '@capsbharg/sparkguide-js';

Targeting.matches(
  { user: { plan: 'free' }, elementExists: '#upgrade-banner' },
  { user: { plan: 'free', role: 'member' } }
); // => true

πŸ“‘ Events

Subscribe with guide.on(event, handler) (returns an unsubscribe function).

EventFires when
flow:starta flow begins
flow:completea flow finishes its last step
flow:dismissa flow is closed early
step:showa step renders β€” (flowId, stepIndex, step)
step:completea step is passed β€” (flowId, stepIndex, step)
checklist:itema checklist item is toggled β€” (checklistId, itemId, done)
checklist:completeevery item in a checklist is done
guide.on('step:show', (flowId, index, step) => {
  analytics.track('onboarding_step_viewed', { flowId, index });
});

🎨 Theming

Pass a theme object at construction β€” everything is exposed as a CSS custom property under the hood, so no separate stylesheet is ever needed:

new SparkGuide({
  theme: {
    primaryColor: '#4f46e5',     // buttons, progress bars, the hotspot dot
    textColor: '#1f2933',
    backgroundColor: '#ffffff',
    overlayColor: 'rgba(15, 23, 42, 0.55)',
    borderRadius: '10px',
    fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
    zIndex: 999999
  }
});

Creating a second SparkGuide instance with a different theme (e.g. after a user switches workspaces) updates the shared stylesheet in place β€” the newest theme always wins.

πŸ’Ύ Persistence & replaying tours

Progress lives in localStorage, namespaced by storageKey, so it's safe to run multiple independent guides (e.g. one per logged-in account) side by side on the same origin.

See it all working together: the live demo exercises every feature on this page against a small mock SaaS app, with a live event log and a targeting sandbox you can poke at.