@math_o_chris/beacon-core
v0.1.0
Published
Browser spotlight / guided-tour library — SVG-mask overlay, tooltips, tour runner. Zero runtime dependencies.
Maintainers
Readme
beacon.js
Browser spotlight / guided-tour library. SVG-mask overlay, tooltips, tour runner, viewport-fixed nav bar, data-attribute API. Zero runtime dependencies.
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-coreESM, 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", witharia-labelledby/aria-describedbylinked 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-describedbypointing at its tooltip while shown. - The tooltip layer is a
aria-live="polite"region so SR users hear each step's content.
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.
