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

@math_o_chris/beacon-core

v0.1.0

Published

Browser spotlight / guided-tour library — SVG-mask overlay, tooltips, tour runner. Zero runtime dependencies.

Readme

beacon.js

Browser spotlight / guided-tour library. SVG-mask overlay, tooltips, tour runner, viewport-fixed nav bar, data-attribute API. Zero runtime dependencies.

npm bundle types license

For a React-first integration see @math_o_chris/beacon-react. For the broader project overview and full docs see the repo root.

Install

pnpm add @math_o_chris/beacon-core
# or
npm install @math_o_chris/beacon-core
# or
yarn add @math_o_chris/beacon-core

ESM, CJS (UMD bundle as Beacon global), and .d.ts types are all shipped. CSS is auto-injected on first activation — no separate stylesheet import required.

Quickstart

import beacon from '@math_o_chris/beacon-core';

const controller = beacon.start({
  id: 'onboarding',
  steps: [
    {
      targets: [
        { target: '#first-button', tooltip: { title: 'Welcome', body: 'This is where it all begins.' } },
      ],
    },
    {
      targets: [{ target: '.sidebar', tooltip: { body: 'And here is where everything lives.' } }],
    },
  ],
  onFinish: () => console.log('tour complete'),
});

controller.on('step', ({ index }) => console.log('on step', index));

The dimmer + nav bar render automatically. Click the chevrons (or press / ) to navigate, click the dimmer / press Esc / hit the close icon to dismiss.

Single-step focus

For a one-off highlight (no tour, no prev/next nav), use focus():

beacon.focus({
  targets: [{ target: '#search', tooltip: { title: 'Try the new search' } }],
});

By default focus mode hides prev/next/dismiss. Pass { showNav: true } to render just the dismiss button.

API

beacon.start(tour, options?) → BeaconController

Begin a multi-step tour at index 0.

type BeaconTour = {
  id?: string;
  steps: BeaconStep[];
  onStart?: () => void;
  onFinish?: () => void;
  onStop?: (reason: 'user' | 'finished' | 'replaced') => void;
};

beacon.focus(step, options?) → BeaconController

Show a single step until dismissed.

beacon.register(root?) → () => void

Scan root (default document) for data-beacon-* markers and wire any data-beacon-trigger buttons. Returns a disposer. A MutationObserver keeps the binding map in sync as triggers are hot-added or removed.

See docs/data-attributes.md for the full attribute reference.

beacon.stop()

Stop whichever controller is currently active. No-op when nothing is active.

BeaconController

interface BeaconController {
  readonly currentStep: number;
  readonly stepCount: number;
  readonly mode: 'tour' | 'focus';

  next(): Promise<void>;
  prev(): Promise<void>;
  goTo(index: number): Promise<void>;
  stop(reason?: 'user' | 'finished' | 'replaced'): void;

  on(event: 'start' | 'step' | 'finish' | 'stop', cb): () => void;
}

The same payloads are also dispatched as CustomEvents on document: beacon:start, beacon:step, beacon:finish, beacon:stop.

Options

interface BeaconOptions {
  showNav?: boolean;                 // default: true (tour) / false (focus)
  navProgress?: 'counter' | 'dots' | 'none';  // default 'counter'
  closeOnOverlayClick?: boolean;     // default true
  closeOnEscape?: boolean;           // default true
  arrowKeys?: boolean;               // default true (tour mode only)
  scrollBehavior?: 'smooth' | 'instant' | 'none';  // default 'smooth'
  animation?: AnimationOptions;
  theme?: BeaconTheme;
  zIndex?: number;                   // default 9999
  ringEnabled?: boolean;             // default false
  ariaLabel?: string;                // default 'Guided overlay'
  container?: HTMLElement;           // overlay parent; default document.body
}

Full type details are in docs/api.md.

Theming

Every visual decision is exposed through a --beacon-* CSS custom property scoped to .beacon-root. To re-skin everything, set them once on :root (or pass them through options.theme as inline values):

:root {
  --beacon-overlay-color: #0b1120;
  --beacon-overlay-opacity: 0.7;
  --beacon-tooltip-bg: #ffffff;
  --beacon-tooltip-border-width: 1px;       /* opt-in; defaults to 0 */
  --beacon-nav-bg: #ffffff;
  --beacon-nav-border-width: 1px;            /* opt-in; defaults to 0 */
  --beacon-hole-radius: 12px;
  --beacon-duration-step: 320ms;
}

Tailwind users can wire their design tokens straight onto these properties:

@layer base {
  :root {
    --beacon-overlay-color: theme(colors.zinc.900);
    --beacon-tooltip-bg: theme(colors.white);
    --beacon-tooltip-color: theme(colors.zinc.800);
    --beacon-tooltip-radius: theme(borderRadius.lg);
  }
}

Built-in theme presets: 'default' (light), 'dark', 'minimal'. Use any other string to declare your own preset and write a CSS rule for .beacon-root[data-preset="<name>"].

See docs/theming.md for every token and a Tailwind example.

Animation

Step transitions interpolate hole geometry (x / y / width / height / rx) via a requestAnimationFrame lerp loop. Beacon pairs holes by target identity first (same selector / element across steps stays in place), then by positional index so single-target spotlights travel between targets. Anything still unpaired fades in/out from its centre.

prefers-reduced-motion: reduce and animated: false collapse all transitions to instant cuts. See docs/animation.md.

Accessibility

  • Overlay is a role="dialog" aria-modal="true", with aria-labelledby / aria-describedby linked to the active tooltip's title and body.
  • Focus is captured before mount and pushed into the nav (Next button); restored to the previous element on stop.
  • Tab / Shift+Tab cycles focus inside the overlay. Tab from outside the dialog re-anchors back inside.
  • Escape dismisses; Arrow keys navigate (tour mode); both can be opted out via options.
  • Each highlighted target receives aria-describedby pointing at its tooltip while shown.
  • The tooltip layer is a aria-live="polite" region so SR users hear each step's content.

See docs/accessibility.md.

Browser support

Targets the last 2 versions of Chrome, Edge, Firefox, and Safari. Public functions are SSR-safe — injectStyles() no-ops in non-browser environments and start/focus throw a clear error when invoked without a document.

License

MIT — see LICENSE.