Skip to content

docs(demos): anchor tour spotlight in the demo so it scrolls with the page - #163

Merged
lesnik512 merged 1 commit into
mainfrom
docs/demo-tour-scroll
Oct 16, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
docs/demo-tour-scroll

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Follow-up to #162. That PR made the tour spotlight re-measure on scroll, which kept it on target but made it jump while scrolling. A frame-by-frame check showed three causes. The ring's transition: all .2s made it trail the dim panels by up to 149px. The position: fixed overlay was updated by a scroll handler a frame after the browser had already scrolled the page. And the coach was clamped to the viewport, so it stuck to the top edge once the target scrolled away.

This PR stops re-positioning on scroll altogether. The ring and coach are now position: absolute inside the .hw-demo container (already position: relative), with coordinates relative to it, so the browser scrolls them with the page and no script runs on scroll. The four .dim panels are replaced by a zero-blur 200vmax spread box-shadow on the ring, which also gives the hole the ring's rounded corners. A transparent fixed .shield keeps blocking clicks outside the spotlight, as the dim panels did. A ResizeObserver on the container and the targets re-places the overlay when layout changes, instead of a resize listener.

Since the coach is no longer pinned to the viewport, each stop now scrolls itself into view: centered when the hole and coach fit on screen, and otherwise scrolled so the coach is fully visible next to the target. It uses an instant scroll under prefers-reduced-motion. The coach goes above the target when there is not enough page left below it. That check measures the page height with the coach hidden, because otherwise the coach's own height counts as room and the placement flips on the next re-measure. The arrow for the "above" placement also gets the border it was missing.

Alternatives looked at: CSS anchor positioning can place and flip the coach (Chrome 125+, Firefox 147+, Safari 26+), but the arrow can only learn which side won through anchored container queries, which ship in Chrome only. Driver.js (MIT) also uses a fixed overlay plus scroll listeners, so it has the same lag. Shepherd.js and Intro.js are AGPL-3.0 with a commercial license. Floating UI only handles positioning, which this needs about 20 lines for.

Testing

Throwaway headless Playwright scripts against mkdocs serve, each run on main and on this branch.

Scroll drift: play each demo to its first stop, wheel-scroll 720px in 60px steps, and record how far the ring and coach move relative to the target on every animation frame.

demo main ring / coach drift branch ring / coach drift
retry 122 / 328 px 0 / 0 px
retry herd 95 / 134 px 0 / 0 px
circuit breaker 95 / 60 px 0 / 0 px
full stack 100 / 60 px 0 / 0 px

Walking every stop of every demo page at 1280x800 and 375x740: on all 24 stops at both sizes, the coach is fully on screen and clicks outside the spotlight land on the shield. The ring is on screen too, except where the target plus coach is taller than the phone viewport. There are no page errors. On main at 375px the ring was off screen on most stops, because stops never scrolled into view. The horizontal overflow at 375px comes from the circuit breaker and full stack demos' own .box elements and is the same on main.

mkdocs build --strict passes.

@lesnik512
lesnik512 merged commit 2355603 into main Oct 16, 2026
13 checks passed
@lesnik512
lesnik512 deleted the docs/demo-tour-scroll branch October 16, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant