npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

just-a-walkthrough

v0.2.1

Published

Framework-agnostic onboarding walkthrough / product tour library with optional React provider & Tailwind/shadcn support.

Readme

just-a-walkthrough

npm version Bundlephobia License Formatted with Biome Linted with Biome Checked with Biome

Framework‑agnostic, zero‑dependency product tour / onboarding walkthrough with optional React helpers.

https://github.com/user-attachments/assets/27a1aff2-ca36-4b82-9b21-62209dff055f

Support us on PeerList!!

Features

  • Spotlight highlight & darkened backdrop (3 panel overlay + focus ring)
  • Accessible keyboard navigation (Esc / Enter / ← →) + focus trap
  • Auto scroll + responsive reposition on scroll/resize/mutations
  • Step hooks (beforeStep / afterStep) & lifecycle callbacks
  • Chain multiple tours with persistence-aware skipping
  • LocalStorage progress persistence & resume support
  • Optional once‑per‑session logic via orchestrator helper
  • Start tours on page open (auto), programmatically, or on click (trigger: 'click' / data-wt-start)
  • Custom tooltip renderer + theming (default, shadcn, tailwind, unstyled) — shadcn works on Tailwind v3 & v4
  • Vue-devtools-style dev panel: wireframe flow + live config editing
  • Works with any DOM (vanilla, React, shadcn, portals)
  • Tiny, tree‑shakeable (no external deps)

Install

npm i just-a-walkthrough
# or
pnpm add just-a-walkthrough
# or
yarn add just-a-walkthrough

Quick Start (Vanilla)

import { startWalkthrough } from 'just-a-walkthrough';

startWalkthrough([
  { selector: '#hero-cta', title: 'Welcome', content: 'Click here to begin.' },
  { selector: '.nav-settings', title: 'Settings', content: 'Manage preferences.' }
]);

React Example

import { useEffect } from 'react';
import { startWalkthrough } from 'just-a-walkthrough';

export function Onboard() {
  useEffect(() => {
    const inst = startWalkthrough([
      { selector: '#dash-metric', title: 'Metrics' },
      { selector: '#create-btn', title: 'Create', content: 'Start something new' },
    ], { persistProgress: true, tourId: 'main-onboarding' });
    return () => inst.destroy();
  }, []);
  return null;
}

Orchestrator (Route-based Tours)

import { registerTours, startAutoMatches } from 'just-a-walkthrough';

registerTours([
  { id: 'home-tour', match: '/home', steps: [ { selector: '#welcome', title: 'Hi!' } ] },
  { id: 'settings-tour', match: '/settings', steps: [ { selector: '#profile', title: 'Profile' } ], oncePerSession: true }
]);

// Call on route change
startAutoMatches({ pathname: window.location.pathname });

API Surface (Core)

startWalkthrough(steps, options) – convenience wrapper returning a Walkthrough instance.

Walkthrough key methods:

  • start(index?) – begin tour (auto called by helper)
  • next()/prev()
  • finish() – mark completed & cleanup
  • skip(reason?)
  • destroy() – cleanup without marking completed

Persistence options (when persistProgress: true & tourId set):

  • Saves { index, completed } in localStorage under __walkthrough:<tourId>
  • Resumes if not completed and resume !== false

Theming

Choose a theme in WalkthroughOptions:

| theme | What it does | | --- | --- | | default | Self-contained injected CSS. No external styles required. | | shadcn | Styles the overlay/ring/tooltip using your shadcn/ui design tokens (CSS variables) at runtime. Recommended for shadcn apps. | | tailwind | Adds Tailwind utility classes (bg-popover, border-primary, …). ⚠️ You must ensure those classes are generated (see note below). | | unstyled | No styling — bring your own via tooltipClass / ringClass / overlayClass. |

shadcn / Tailwind support (v3 and v4)

The overlay is created at runtime, so Tailwind's JIT never "sees" utility classes the library adds — with theme: 'tailwind' they get purged and the tour renders unstyled unless you safelist them. theme: 'shadcn' avoids this entirely by referencing your design tokens as CSS variables directly, so it works regardless of purge and inherits your light/dark theme automatically.

shadcn's token format differs between Tailwind majors, so pick tokenColorFormat:

// Tailwind v4 / current shadcn (tokens are full OKLCH colors)
startWalkthrough(steps, { theme: 'shadcn' }); // tokenColorFormat: 'raw' (default)

// Tailwind v3 shadcn (tokens are bare `H S L` triples)
startWalkthrough(steps, { theme: 'shadcn', tokenColorFormat: 'hsl' });

Remap which CSS variables are used with themeVars:

startWalkthrough(steps, {
  theme: 'shadcn',
  themeVars: { primary: '--accent', ring: '--accent' },
});

Roles: popover, popoverForeground, border, primary, primaryForeground, ring (defaulting to --popover, --popover-foreground, --border, --primary, --primary-foreground, --ring).

If you prefer theme: 'tailwind' (utility classes), add the library to your Tailwind content / safelist so the classes are generated — e.g. on v4: @source "../node_modules/just-a-walkthrough/dist";

Chain Multiple Tours

import { WalkthroughChain } from 'just-a-walkthrough';
new WalkthroughChain([
  { id: 'a', steps: [ { selector: '#x' } ] },
  { id: 'b', steps: [ { selector: '#y' } ], options: { persistProgress: true, tourId: 'b' } }
]).start();

Completed persistent tours are skipped automatically.

React Integration (Provider & Hook)

For React apps you can wrap your tree with the WalkthroughProvider to get easy access to start and chain helpers plus reactive state (current index, active flag):

import { WalkthroughProvider, useWalkthrough } from 'just-a-walkthrough/react';

function LaunchTourButton() {
  const { start, active } = useWalkthrough();
  return (
    <button
      disabled={active}
      onClick={() => start([
        { selector: '#logo', title: 'Logo' },
        { selector: '#settings', title: 'Settings' },
      ], { persistProgress: true, tourId: 'react-main' })}
    >Start Tour</button>
  );
}

export function App() {
  return (
    <WalkthroughProvider>
      <LaunchTourButton />
      {/* rest of app */}
    </WalkthroughProvider>
  );
}

Auto start on mount:

<WalkthroughProvider autoStart={{ steps, options }} />

React Route Orchestrator Component

If you want automatic starting of registered route-based tours when the location changes, use the RouteOrchestrator helper:

import { RouteOrchestrator } from 'just-a-walkthrough/react';
import { registerTours } from 'just-a-walkthrough';

registerTours([
  { id: 'home-tour', match: '/home', trigger: 'auto', steps: [ { selector: '#home-title', title: 'Home' } ] },
  { id: 'profile-tour', match: /\/users\//, trigger: 'auto', steps: [ { selector: '#avatar', title: 'Avatar' } ], order: 10 },
]);

function Routes({ pathname }: { pathname: string }) {
  return (
    <>
      <RouteOrchestrator pathname={pathname} chain onStartIds={ids => console.log('Started tours', ids)} />
      {/* your routed UI */}
    </>
  );
}

Lazy load a module containing tour registrations before matching (pass a module specifier string, which is import()-ed internally):

<RouteOrchestrator pathname={pathname} dynamicModule="./tours" />

Mounting RouteOrchestrator also activates click triggers (see below).

Click Triggers (start a tour on click)

Besides auto (start on route match) and manual (programmatic only), a tour can use trigger: 'click' to start when a matching element is clicked. Two ways to wire it:

1. Declarative data-wt-start attribute — works on any element once the delegated listener is active (mount RouteOrchestrator, or call bindTourTriggers() yourself):

registerTour({ id: 'help', trigger: 'click', match: '*', steps: [/* … */] });

// anywhere in your UI (clicks on descendants count too):
<button data-wt-start="help">Take the tour</button>

2. A triggerSelector on the registration (no attribute needed):

registerTour({
  id: 'help',
  trigger: 'click',
  triggerSelector: '#help-button',
  match: '*',
  steps: [/* … */],
});

React helpers:

import { TourTrigger, useTourTrigger } from 'just-a-walkthrough';

<TourTrigger tourId="help">Take the tour</TourTrigger>;
// or
const startHelp = useTourTrigger('help');
<button onClick={startHelp}>Take the tour</button>;

Notes:

  • Clicks are de-duplicated: a second click while the tour is running is ignored.
  • By default click starts honor gating (oncePerSession / skipIfCompleted / condition). Set ignoreGatingOnClick: true on the tour to always start on click.
  • click (and manual) tours are excluded from startAutoMatches / chainAutoMatches.

Dev Panel (Development Only)

The optional WalkthroughDevPanel is a Vue-devtools-style floating inspector for tours, with three tabs:

  • Tours — list registered tours (matcher summary, completed/pending/running), start/ restart/reset each, and jump to any step of the running tour.
  • Wireframe — an SVG graph of every tour's step flow (grouped by match); the live step is highlighted and nodes of a running tour are clickable to jump.
  • Config — live-edit the selected tour's options (theme, backdrop, colors, behavior toggles). Changes apply immediately to a running tour and persist across restarts/ reloads (localStorage __wt_devpanel_tweaks:<id>).

It reads live state from the core active-instances registry, so it reflects tours started by any means — provider, orchestrator, click trigger, or a direct call.

import { WalkthroughDevPanel } from 'just-a-walkthrough/react';

function Root() {
  return (
    <>
      {/* app UI */}
      {import.meta.env.DEV && <WalkthroughDevPanel chainMatches />}
    </>
  );
}

Also:

  • Run Matches (or Chain Matches if chainMatches prop true); Refresh; Reset All.
  • Collapsible; collapsed state stored in localStorage (__wt_devpanel_collapsed).

Do NOT ship this to production (reveals internal tour structure).

Debugging / Diagnostics

Instrumentation is available but silent by default. To enable and inspect internal events you can use one of:

  1. Build-time env var (e.g. with Vite): JUST_A_WALKTHROUGH_DEBUG=true
  2. Runtime flag: window.__JUST_A_WALKTHROUGH_DEBUG = true
  3. API call: enableDebug(true)

Events recorded include categories: walkthrough (start, step, resolved, missing, finish, skip), orchestrator (registration, match, chain), react (route effects, dynamic loads).

import { enableDebug, dumpWalkthroughDebug, printWalkthroughDebug } from 'just-a-walkthrough/debug';

enableDebug(true);
// run some tours ...

// Safe summary only (counts + meta, no raw events)
printWalkthroughDebug();

// Full detail (explicit opt-in) with redaction example
printWalkthroughDebug({
  full: true,
  redact: e => ({ ...e, data: undefined })
});

const snapshot = dumpWalkthroughDebug(); // programmatic access

Security: by default printWalkthroughDebug() prints only aggregate counts (avoids leaking selectors or user data). Pass { full: true } consciously in trusted environments.

HTML Content Sanitization: step content strings are sanitized by default (removes <script>, <style>, <iframe> etc., strips event handler attributes like onclick, and blocks unsafe URL schemes such as javascript:). This mitigates XSS if tour definitions incorporate user‑generated text. If you absolutely trust the source, set allowUnsafeHTML: true on an individual step to bypass the sanitizer—but prefer leaving it enabled.

You can also access a console proxy wtDebug which is a no-op unless debug is enabled:

import { wtDebug } from 'just-a-walkthrough/debug';
wtDebug.log('current tour id', currentId);

Advanced Usage Notes

  • Use waitMs: 0 (or global stepWaitMs: 0) for elements that are guaranteed to be present to avoid unnecessary polling.
  • Prefer chaining tours when you have progressive disclosure flows; persistent tours auto-skip if already completed, keeping chains idempotent.
  • When dynamically removing highlighted elements mid-step (e.g. route transitions), the MutationObserver repositions but if the element disappears the next navigation call will resolve again. Consider guarding with required steps if element is critical.

Accessibility Notes

  • Focus ring container traps tab order (unless disableFocusTrap: true)
  • Live region announces step titles (aria-live="polite")
  • Esc always available (when keyboard: true)

Zero-Wait Configuration

Specify stepWaitMs: 0 and/or per-step waitMs: 0 to disable polling for elements (single lookup). stepPollIntervalMs is clamped to a minimum of 1ms internally when waiting.

Changelog

This project maintains an automated changelog based on git commits and package.json versions. See CHANGELOG.md for release history and docs/CHANGELOG_GENERATION.md for details on the generation process.

Development

npm i
npm run dev      # playground
npm test         # vitest (jsdom)
npm run build    # library build + types
npm run changelog # generate/update changelog based on commits

Publishing (Maintainers)

npm run build && npm test
npm version patch   # or minor / major
npm run changelog   # update changelog with new version
git push && git push --tags
npm publish --access public

License

AGPL-3.0-only – see LICENSE. For commercial / alternative licensing reach out.


Contributions welcome. Open an issue or PR with ideas / improvements.