Calling destroy() on a driver that was never started or already
destroyed would strip body classes and highlights belonging to
another active instance.
Fixes#504
The demo's beacon placement on the hero didn't sit right, and the
homepage grid works fine without it; the hints guide and its live
samples remain the place to try hints. The shorter 'and more ...'
label stays.
The homepage examples grid gains a Hints button that toggles three
beacons on the hero: one plain dismissable hint, one whose button starts
a short tour through onButtonClick (showing the two features hand off to
each other), and one whose button leads to the hints guide. The last-row
label shrinks from 'and much more ...' to 'and more ...' so the row
keeps to one line.
The guide's hint-launches-tour example now uses buttonText plus
onButtonClick instead of hand-building a button in onPopoverRender,
which stays mentioned for additional buttons.
The built-in button always dismissed; repurposing it meant hiding it and
rebuilding one in onPopoverRender. onButtonClick (per hint or instance
wide, hint wins) now runs instead of dismissing, mirroring how a tour's
onNextClick takes over the default advance; call dismiss() from the hook
to keep the old behavior. Custom and extra buttons remain the
onPopoverRender pattern, same as tours.
Basic Usage gains a live hints demo with its code, replacing a dangling
paragraph that promised a single-step tour example which never followed.
Installation covers the hints entry for both npm (separate import,
self-contained stylesheet) and the CDN (hints.iife.js, hints.css, and
the window.driverHints global). Theming points at the beacon CSS
variables and the Styling Hints examples. The landing marquee names
tours, highlights and hints, and the meta description mentions hints.
Hints were only covered by their own guide. Configuration gains the
HintsConfig and DriverHint sections in the same annotated-type style as
the tour's, and API Reference gains the hints() factory with every
instance method. Both cross-link the guide, and the API page links the
new configuration anchor. Also replaces a pre-existing em dash in the
onDoneClick note.
show() now mounts hints whose elements have appeared since the last
call, which the docs already promised but only a full hide()/show()
delivered; mountHints() skips what is already on the page, making
show() naturally idempotent and simpler.
The closePopover()/close() pair collapses into one function, the
scroll and resize listeners use requireRefresh directly instead of
wrapper closures, and config/dismissed become consts.
Comments get a pass: the HintsConfig overlay comment still described
the pre-cutout semantics (beacons above the overlay) and is corrected;
obvious comments are dropped, the load-bearing ones tightened, and em
dashes across source comments replaced per Kamran's preference.
box-shadow follows the popover's rectangular box, so the protruding
arrow cast nothing and read as a flat notch against whatever sat behind
it. The hint popover now uses a drop-shadow filter instead, which
follows the rendered silhouette: the rounded box and the arrow cast a
single continuous shadow, with no border tricks and no assumptions
about the theme's colors. Tours are untouched since their overlay
already gives the arrow contrast.
The hero example on the guide turns the optional overlay on, so the
first interaction shows the spotlight, and its code block now matches
what the demo runs. The lead-in names the overlay as optional so the
default stays clear.
The Dimming the page sample moves to the top of Styling Hints. The em
dashes across the hints docs copy, the demo component and the changelog
entries are rephrased with plain punctuation.
With the overlay up, an open hint now reads exactly like a tour step:
the popover anchors to the element (clearing the cutout ring and lining
up with its edge) instead of hanging off the beacon, and the beacon
steps aside while its popover is up — the spotlight does the pointing.
Suggested by Kamran: once the dot has been clicked, it is just noise
inside the cutout. Without the overlay nothing changes; the dot remains
the anchor and the toggle. Dim-click, Escape and Got it all still close.
The hint arrow also grows to 14px (7px borders) — the tour's 10px
triangle read small next to a 24px beacon. Tours are untouched. The
arrow placement code now measures the arrow's real box instead of
assuming the constant, so restyled arrows keep their tip on target;
the beacon-shift constant is the one place the size is assumed, since
it runs before the popover exists.
The overlay is now the same spotlight the tour draws: an svg dim with a
rounded cutout around the open hint's element (the path generator moves
out of the tour's context into a pure function both share). The element
stays visible and interactive inside the cutout — the svg lets pointer
events through and only the dimmed path catches them, so clicking the
dim still closes the hint.
The other beacons now wait under the overlay instead of floating above
it; only the open hint's beacon (via its aria-expanded state) and its
popover rise above. The cutout tracks the element on scroll and resize.
The arrow's drop-shadow halo is gone: it read as a dirty outline against
light pages — tours never needed one because the arrow sits against the
dark overlay, and hints now either touch the colored beacon (no overlay)
or sit against the dim (overlay), so the plain white triangle works in
both.
The guide's live sample dropped the docs theme so readers see the
default look first. New Styling Hints page under Examples: beacon color
and size through CSS variables and per-beacon classes, static beacons,
custom and hidden dismiss buttons, the overlay focus mode, and popover
theming. HintsSample takes an id prefix and instance config so several
samples share a page.
Three fixes to how hints look and behave:
The popover arrow used to land beside the dot: it sits a fixed corner
inset from the popover's edge, while start/end alignment put the popover
edge flush with the beacon. The position options now pass the difference
between the arrow tip and the beacon's half-size as the alignment
pull-back, so the arrow points at the beacon's center. The arrow also
gets a faint halo — it is a white triangle designed to sit against a
tour's darkened overlay, and vanished on light pages without one.
The default beacon grows from 20px to 24px (still --driver-hint-size),
and defaults to a lighter indigo.
New opt-in overlay: overlay: true dims the page while a hint is open
(overlayColor/overlayOpacity to taste). Beacons and the popover sit
above it so hints stay clickable, and clicking the dimmed page closes
the hint like any outside click. Verified in a real browser — beacons
float above the dim, arrow centered on the dot.
A Hints guide in the docs Introduction section with a live sample
component (self-contained mock UI the beacons attach to), covering the
close-vs-dismiss semantics, the localStorage persistence recipe, beacon
placement/styling and the full options and methods reference. The docs
app gains driver.js/hints and hints.css source aliases for HMR, same as
the playground.
The Unreleased changelog section lists the hints entry, the new CSS
variables, the sideEffects field, the shared popover layer and the
tsdown build migration — which also makes the release flow green: the
release aborts on an empty Unreleased section.
packages/driver is the library (still published as driver.js, with
repository.directory set); playground and docs move to apps/. One root
pnpm workspace and lockfile replace the three standalone projects, with
turbo orchestrating build/test/lint:package (apps depend on the library
build; local + CI caching via .turbo).
Apps keep importing the library from source for HMR — the aliases now
point at ../../packages/driver/src and each app gains a workspace:*
dependency so the task graph knows the edge. The dev-server fs scope
widens to the repo root accordingly.
Releases run from packages/driver (root `pnpm release` delegates). The
changelog still lives in the docs app, so the release scripts reach it
via ../../../apps/docs and the after:bump hook stages it explicitly —
release-it no longer sits above both directories.
The merged workspace settings keep sharp unbuilt on purpose (prebuilt
binaries; building from source needs node-gyp and fails) — the docs app
had this right and the playground setting was the accident. postcss and
postcss-scss were unreferenced devDependencies and are dropped; prettier
moves to the root as the only shared tool.
Adversarial review of the primitive refactor caught a within-contract
regression: the old renderPopover committed the popover to state and
then ran onPopoverRender, so hooks reading opts.state.popover saw the
freshly rendered popover. After the refactor the state write happened
once the primitive returned — inside the hook, opts.state.popover was
undefined on first render and the previous step's detached popover on
transitions. Commit the state in the onRender adapter, before the user
hook, and pin it with a test.