Lightweight library for product tours, highlights, and contextual help in Tampermonkey userscripts. Inspired by driver.js, designed for the userscript ecosystem. Zero dependencies, Auto-injects CSS, Sandbox-compatible
This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyfork.org/scripts/567414/1909205/TamperGuide.js
Lightweight library for product tours, highlights, and contextual help in Tampermonkey userscripts.
Inspired by driver.js, designed specifically for the userscript ecosystem.
waitFor) and conditional steps (when)advanceOn) & non-blocking hotspotslocalStorage or GM storage)default, dark, minimal, rounded)Add TamperGuide using @require in your userscript header:
// @require https://cdn.jsdelivr.net/gh/UNKchr/[email protected]/tamperguide/tamperGuide.js
(Alternative: use your Greasy Fork update URL or GitHub raw link).
// ==UserScript==
// @name Tour Example
// @match https://example.com/*
// @require https://cdn.jsdelivr.net/gh/UNKchr/[email protected]/tamperguide/tamperGuide.js
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const guide = tamperGuide({
showProgress: true,
animate: true,
steps: [
{
popover: {
title: 'Welcome',
description: 'This short tour will introduce the main interface.',
},
},
{
element: '#search-input',
popover: {
title: 'Search Bar',
description: 'Type here to find content quickly.',
side: 'bottom',
},
},
],
});
guide.drive();
})();
const guide = tamperGuide();
guide.highlight({
element: '#profile-btn',
popover: {
title: 'Account Settings',
description: 'Manage your preferences here.',
side: 'left',
},
});
Pass options to tamperGuide(options). All fields are optional.
| Option | Type | Default | Description |
|---|---|---|---|
steps |
Array |
[] |
Array of step configuration objects. |
theme |
string |
'default' |
Theme: 'default', 'dark', 'minimal', 'rounded'. |
animate |
boolean |
true |
Enable fade and slide transitions. |
overlayColor |
string |
'#000' |
Dimmed overlay color. |
overlayOpacity |
number |
0.7 |
Opacity from 0 to 1. |
stagePadding |
number |
10 |
Padding around highlighted element (px). |
stageRadius |
number |
5 |
Rounded border radius of the cutout (px). |
allowClose |
boolean |
true |
Close via Escape key or backdrop click. |
allowKeyboardControl |
boolean |
true |
Enable keyboard navigation. |
allowBackdropInteraction |
boolean |
false |
Allow clicking outside the popover. |
disableActiveInteraction |
boolean |
false |
Prevent clicks on the highlighted element. |
smoothScroll |
boolean |
true |
Smooth scroll to target elements. |
showProgress |
boolean |
false |
Show step counter in footer. |
progressText |
string |
'{{current}} of {{total}}' |
Counter template. |
showButtons |
Array<string> |
['next', 'previous', 'close'] |
Visible built-in buttons. |
buttons |
Array |
undefined |
Custom buttons list (overrides showButtons). |
nextBtnText |
string |
'Next →' |
Next button text. |
prevBtnText |
string |
'← Previous' |
Previous button text. |
doneBtnText |
string |
'Done ✓' |
Final step button text. |
closeBtnText |
string |
'×' |
Close button text. |
popoverClass |
string |
'' |
Custom CSS class names. |
popoverOffset |
number |
10 |
Distance from element to popover (px). |
autoRefresh |
boolean |
false |
Auto-reposition on DOM mutations. |
autoRefreshInterval |
number |
300 |
MutationObserver debounce (ms). |
persist |
boolean |
false |
Persist progress across page loads. |
persistKey |
string |
'' |
Unique identifier (required if persist is true). |
persistStorage |
string |
'localStorage' |
'localStorage' or 'GM' (needs @grant GM_*). |
persistExpiry |
number |
604800000 |
Expiration in ms (default: 7 days; 0 = never). |
{
element: '#target', // CSS selector, Element, or () => Element
id: 'step-settings', // Optional unique step identifier
ariaLabel: 'Settings step', // Screen reader description
when: () => isUserLoggedIn, // Boolean callback; skips step if false
waitFor: { // Polls for asynchronous elements
timeout: 5000,
pollInterval: 200,
},
advanceOn: { // Auto-advance on user action
event: 'click',
selector: '#target-btn', // Optional; defaults to step element
},
popover: {
title: 'Step Title',
description: 'Detailed description.',
side: 'bottom', // 'top' | 'right' | 'bottom' | 'left'
align: 'start', // 'start' | 'center' | 'end'
showButtons: ['next', 'previous'],
buttons: [ // Custom button definition
{
text: 'Skip',
variant: 'link', // 'primary' | 'secondary' | 'link' | 'danger'
onClick: (el, step, ctx) => ctx.driver.destroy(),
},
'next',
],
},
// Per-step lifecycle hooks
onHighlightStarted: (el, step, ctx) => {},
onHighlighted: (el, step, ctx) => {},
onDeselected: (el, step, ctx) => {},
}
| Method | Description |
|---|---|
guide.drive(index?: number) |
Start tour (resumes progress if persisted). |
guide.moveNext() |
Advance to next step. |
guide.movePrevious() |
Go back to previous step. |
guide.moveTo(index: number) |
Jump to step by index. |
guide.moveToStep(id: string) |
Jump to step by its id. |
guide.highlight(step) |
Spotlight a single element. |
guide.refresh() |
Recalculate cutout and popover positions. |
guide.destroy() |
Close tour and remove all DOM nodes/listeners. |
| Method | Return Type | Description |
|---|---|---|
guide.isActive() |
boolean |
True if guide is running. |
guide.isFirstStep() |
boolean |
True if on the first step. |
guide.isLastStep() |
boolean |
True if on the final step. |
guide.getActiveIndex() |
number |
Active step index. |
guide.getActiveStep() |
object |
Active step configuration. |
guide.getActiveElement() |
Element |
Currently highlighted DOM node. |
guide.getStepCount() |
number |
Total number of configured steps. |
guide.isCompleted() |
boolean |
True if tour was finished previously. |
guide.resetProgress() |
void |
Clear saved persistence state. |
// Add non-blocking pulsing indicator
guide.addHotspot({
element: '#new-feature',
tooltip: 'Explore new tools!',
side: 'bottom',
pulse: true,
pulseColor: '#ef4444',
dismissOnClick: true,
autoDismiss: 10000,
});
guide.removeHotspot('#new-feature');
guide.removeAllHotspots();
| Hook | Cancellable | Description |
|---|---|---|
onHighlightStarted |
No | Before scrolling or rendering step. |
onHighlighted |
No | After popover and cutout render. |
onDeselected |
No | When navigating away from step. |
onDestroyStarted |
Yes (return false) |
Before destroying guide. |
onDestroyed |
No | After guide cleanup is complete. |
onNextClick |
Yes (return false) |
When next button/key is triggered. |
onPrevClick |
Yes (return false) |
When previous button/key is triggered. |
onCloseClick |
Yes (return false) |
When close button/Esc is pressed. |
onPopoverRender |
No | Modify popover DOM before display. |
onStepChange |
No | Analytics: { stepIndex, duration, direction }. |
onTourComplete |
No | Analytics: { completed, totalDuration, stepsVisited }. |
| Shortcut | Action |
|---|---|
ArrowRight / Tab |
Next step (focus trapped inside popover). |
ArrowLeft / Shift + Tab |
Previous step. |
Escape |
Close tour. |
Errors throw TamperGuideError with a .code property:
INVALID_CONFIG: Malformed options or unknown keys.INVALID_STEP: Step missing required attributes.NO_STEPS: drive() invoked with empty steps array.INVALID_STEP_INDEX: Target step index or ID does not exist.ELEMENT_NOT_FOUND / WAIT_TIMEOUT: Target selector not found (warning).PERSISTENCE_ERROR: Storage failure (warning).MIT © UNKchr