Skip to content

fix(#3726): highlight last TOC heading when scrolled to bottom#3727

Merged
chrisolsen merged 1 commit intodevfrom
claude/fix-toc-bottom-scroll-KKTU5
Apr 7, 2026
Merged

fix(#3726): highlight last TOC heading when scrolled to bottom#3727
chrisolsen merged 1 commit intodevfrom
claude/fix-toc-bottom-scroll-KKTU5

Conversation

@twjeffery
Copy link
Copy Markdown
Collaborator

@twjeffery twjeffery commented Mar 31, 2026

Summary

  • Fixes the sticky TOC highlighting the wrong section for short sections near the bottom of the page (e.g. "Get started" on the workspace page)
  • Adds a bottom-of-page check in handleScroll: when window.innerHeight + window.scrollY >= document.body.scrollHeight - 10, the last visible heading is set as active instead of relying on the 120px threshold
Screen.Recording.2026-03-31.at.3.07.21.PM.mov

Closes #3726

Test plan

  • Navigate to a docs page with a short final section (e.g. workspace example page)
  • Scroll to the very bottom of the page — the last TOC item should be highlighted
  • Click the last TOC item — it should become active
  • Scroll back up — TOC highlighting should still work normally for all other sections

https://claude.ai/code/session_01Rcc9hcmnjnE8vdXhiz7VMM

@twjeffery twjeffery changed the title Fix TableOfContents active heading when scrolled to bottom fix(#3726): highlight last TOC heading when scrolled to bottom Mar 31, 2026
@Spark450 Spark450 added the P3 Priority 3 (nice to have): Valuable, but safe to release after launch. label Mar 31, 2026
@twjeffery twjeffery requested a review from Spark450 March 31, 2026 21:09
Copy link
Copy Markdown
Collaborator

@Spark450 Spark450 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is certainly working better but it looks like the selection "bounces" a bit then settles.
https://jam.dev/c/08f2e673-e30e-4d7d-a609-a1a81e62637a

@twjeffery
Copy link
Copy Markdown
Collaborator Author

I think it is certainly working better but it looks like the selection "bounces" a bit then settles. jam.dev/c/08f2e673-e30e-4d7d-a609-a1a81e62637a

I think that's expected for what it's fixing and doing. This is a fix to make it change (override what's set from the scroll position) to the last item if scroll is at the bottom. It's kind of like, it follows the heading based on the scroll position, and then if the scroll is at the bottom, then set it to bottom as active.

@twjeffery twjeffery requested a review from ArakTaiRoth April 1, 2026 15:40
Comment on lines +86 to +99
// When scrolled to the bottom, activate the last visible heading
const atBottom = window.innerHeight + window.scrollY >= document.body.scrollHeight - 10;
if (atBottom) {
let lastVisibleId: string | null = null;
for (const heading of headings) {
if (!isVisible(heading)) continue;
const id = heading.getAttribute("id");
if (id) lastVisibleId = id;
}
if (lastVisibleId) {
setActiveId(lastVisibleId);
return;
}
}
Copy link
Copy Markdown
Collaborator

@chrisolsen chrisolsen Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are scrolling to the bottom, is there any reason you can't just set the last of the headings as the current, rather than this logic + calculations? Is there ever a time that the last heading isn't visible when you scroll to the bottom of the screen?

const atBottom = window.innerHeight + window.scrollY >= document.body.scrollHeight - 10;
if (atBottom) {
	const lastHeading = headings[headings.length - 1];
    const lastVisibleId = lastHeading.getAttribute("id");
    setActiveId(lastVisibleId);
    return;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. The reason we need to filter is that headings here is all the matching headings in the DOM, not just the active tab. On component pages we have 4 tabs (Properties, Examples, Usage, Accessibility) and the headings from all of them are in the DOM at once. If we just grabbed the last one we'd always end up on a heading from the Accessibility tab no matter which tab the user is on.

You're right that we were doing more work than we needed to though. I refactored it to filter visible headings once at the top and both branches reuse that. It should be simpler now.

@Spark450 Spark450 self-requested a review April 1, 2026 22:38
Copy link
Copy Markdown
Collaborator

@Spark450 Spark450 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this approach is valid I think the behaviour is acceptable.

When the page is scrolled to the bottom, short sections near the end
never reach the 120px threshold, so the TOC highlights the wrong item.
Detect bottom-of-page and activate the last visible heading instead.
@twjeffery twjeffery force-pushed the claude/fix-toc-bottom-scroll-KKTU5 branch from f8e1fb1 to fcf3c5b Compare April 7, 2026 16:58
@twjeffery twjeffery requested a review from chrisolsen April 7, 2026 17:41
@chrisolsen chrisolsen merged commit 2736224 into dev Apr 7, 2026
4 checks passed
@chrisolsen chrisolsen deleted the claude/fix-toc-bottom-scroll-KKTU5 branch April 7, 2026 17:49
@chrisolsen
Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 2.0.0-dev.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Priority 3 (nice to have): Valuable, but safe to release after launch. released on @dev

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docs: sticky inline TOC highlights wrong section for short sections near bottom of page

4 participants