Skip to content

fix(toc): only auto-scroll the toc's own scroll container - #3542

Merged
segunadebayo merged 6 commits into
chakra-ui:mainfrom
tad-bit-better:fix/toc-autoscroll-nearest
Oct 26, 2026
Merged

segunadebayo merged 6 commits into
chakra-ui:mainfrom
tad-bit-better:fix/toc-autoscroll-nearest

Conversation

@tad-bit-better

@tad-bit-better tad-bit-better commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3535

autoScrollToc used scrollIntoView({ block: "nearest" }), which scrolls every scrollable ancestor. When the nav sits inside the same scroll container as the content, that also scrolls the content scroller and cancels the smooth scroll from a link click.

Now it finds the nearest scroll container of the active item inside the TOC root and scrolls only that, using a new scrollToNearest helper in dom-query (next to scrollToElement, as suggested in the issue). It accounts for borders, scroll-padding and scroll-margin, and does nothing when the item is already visible. If the TOC doesn't scroll on its own, nothing is scrolled, which also covers the page jumping to show the nav on short windows.

Added a toc/nested-nav example (sticky nav inside the content scroller) and the first TOC e2e tests:

  • clicking a link scrolls all the way to its heading (fails on main, passes here)
  • auto-scroll still keeps the active item visible by scrolling only the nav

Before

before-toc-autoscroll.mp4

After

after-toc-autoscroll.mp4

@changeset-bot

changeset-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f0a270b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 85 packages
Name Type
@zag-js/toc Patch
@zag-js/dom-query Patch
@zag-js/core Patch
@zag-js/accordion Patch
@zag-js/angle-slider Patch
@zag-js/avatar Patch
@zag-js/carousel Patch
@zag-js/cascade-select Patch
@zag-js/checkbox Patch
@zag-js/clipboard Patch
@zag-js/collapsible Patch
@zag-js/color-picker Patch
@zag-js/combobox Patch
@zag-js/date-input Patch
@zag-js/date-picker Patch
@zag-js/dialog Patch
@zag-js/drawer Patch
@zag-js/editable Patch
@zag-js/file-upload Patch
@zag-js/floating-panel Patch
@zag-js/hover-card Patch
@zag-js/image-cropper Patch
@zag-js/listbox Patch
@zag-js/marquee Patch
@zag-js/menu Patch
@zag-js/navigation-menu Patch
@zag-js/number-input Patch
@zag-js/pagination Patch
@zag-js/password-input Patch
@zag-js/pin-input Patch
@zag-js/popover Patch
@zag-js/presence Patch
@zag-js/progress Patch
@zag-js/qr-code Patch
@zag-js/radio-group Patch
@zag-js/rating-group Patch
@zag-js/scroll-area Patch
@zag-js/select Patch
@zag-js/signature-pad Patch
@zag-js/slider Patch
@zag-js/splitter Patch
@zag-js/steps Patch
@zag-js/switch Patch
@zag-js/tabs Patch
@zag-js/tags-input Patch
@zag-js/timer Patch
@zag-js/toast Patch
@zag-js/toggle-group Patch
@zag-js/toggle Patch
@zag-js/tooltip Patch
@zag-js/tour Patch
@zag-js/tree-view Patch
@zag-js/aria-hidden Patch
@zag-js/auto-resize Patch
@zag-js/dismissable Patch
@zag-js/focus-trap Patch
@zag-js/focus-visible Patch
@zag-js/hotkeys Patch
@zag-js/i18n-utils Patch
@zag-js/interact-outside Patch
@zag-js/popper Patch
@zag-js/remove-scroll Patch
@zag-js/scroll-snap Patch
@zag-js/preact Patch
@zag-js/react Patch
@zag-js/solid Patch
@zag-js/svelte Patch
@zag-js/vanilla Patch
@zag-js/vue Patch
@zag-js/async-list Patch
@zag-js/file-utils Patch
@zag-js/anatomy-icons Patch
@zag-js/anatomy Patch
@zag-js/docs Patch
@zag-js/store Patch
@zag-js/types Patch
@zag-js/collection Patch
@zag-js/color-utils Patch
@zag-js/utils Patch
@zag-js/date-utils Patch
@zag-js/highlight-word Patch
@zag-js/json-tree-utils Patch
@zag-js/live-region Patch
@zag-js/rect-utils Patch
@zag-js/stringify-state Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
zag-nextjs Ready Ready Preview Oct 26, 2026 10:54am UTC
zag-solid Ready Ready Preview Oct 26, 2026 10:54am UTC
zag-svelte Ready Ready Preview Oct 26, 2026 10:54am UTC
zag-vue Ready Ready Preview Oct 26, 2026 10:54am UTC
1 Skipped Deployment
Project Deployment Actions Updated
zag-website Ignored Ignored Preview Oct 26, 2026 10:54am UTC

Request Review

- Find the nearest ancestor that can actually scroll within the TOC.
- Resolve percentage scroll padding against the scrollport.
- Don't scroll items that already span the view.
- Search for the scroll container within the nav, so the content scroller is never picked.
- Measure in the container's layout pixels so transforms don't skip the scroll.
- Add getNearestScrollOffset, which computes the scroll position without scrolling.
- Handle both axes in scrollToNearest.
- Scroll instantly when the user prefers reduced motion.
Keeps auto-scroll working with the older markup that spreads the root props on the nav.
- Find the TOC's scroll container with getScrollParent.
- Stop at the element that holds the headings.
- Add isScrollable and getScrollParent to dom-query.

This branch was successfully deployed

4 active deployments
Preview – zag-solid — f0a270bd Deployed Oct 26, 2026 by vercel[bot]
Preview – zag-vue — f0a270bd Deployed Oct 26, 2026 by vercel[bot]
Preview – zag-svelte — f0a270bd Deployed Oct 26, 2026 by vercel[bot]
Preview – zag-nextjs — f0a270bd Deployed Oct 26, 2026 by vercel[bot]
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.

toc: autoScroll cancels link-click smooth scroll when the nav is inside the scroll container

2 participants