Skip to content

Fix nav indicator animation when scrolled#12

Merged
jakebodea merged 2 commits intodevfrom
fix/nav-indicator-scroll
Feb 8, 2026
Merged

Fix nav indicator animation when scrolled#12
jakebodea merged 2 commits intodevfrom
fix/nav-indicator-scroll

Conversation

@jakebodea
Copy link
Owner

@jakebodea jakebodea commented Feb 8, 2026

Summary

  • Replaced layoutId-based nav indicator with a single persistent motion.span that animates left/width using offsetLeft/offsetWidth (parent-relative, scroll-independent)
  • The old layoutId approach converted viewport coords to document coords via scrollY, causing a phantom vertical animation for the sticky nav when scroll position changed between measurements
  • Also scrolls to top on nav click/keyboard shortcut for consistent UX

Closes #9

Test plan

  • Navigate between tabs while at the top of a page — indicator slides horizontally as before
  • Scroll down on any page, then click a different tab — indicator should slide horizontally without bouncing from below
  • Test keyboard shortcuts (1-5) while scrolled down — same smooth horizontal animation
  • Test mobile menu navigation while scrolled down

🤖 Generated with Claude Code


Note

Low Risk
UI-only changes to navigation animation and click/keyboard handlers; low risk aside from minor layout/animation regressions on resize or tab measurement.

Overview
Fixes the desktop top-nav active indicator animation when the page is scrolled by replacing the per-link layoutId indicator with a single persistent motion.span that animates left/width based on measured offsetLeft/offsetWidth via a container ref + useLayoutEffect.

Adds window.scrollTo(0, 0) on desktop nav clicks, mobile menu nav clicks, and numeric keyboard shortcuts to ensure navigation always starts at the top of the new page.

Written by Cursor Bugbot for commit 3b60838. This will update automatically on new commits. Configure here.

Replace layoutId-based indicator with a single persistent motion.span
that animates left/width using offsetLeft/offsetWidth (parent-relative,
scroll-independent). The old approach used Framer Motion layoutId which
converts viewport coords to document coords via scrollY — causing a
phantom vertical animation for sticky elements when scroll position
changed between measurements.

Also scroll to top on nav click/keyboard for consistent UX.

Closes #9

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Contributor

vercel bot commented Feb 8, 2026

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

Project Deployment Actions Updated (UTC)
personal-website Ready Ready Preview, Comment Feb 8, 2026 3:45am

Resolve conflict: keep indicator fix, accept ThemeToggle extraction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jakebodea jakebodea merged commit 445ba6b into dev Feb 8, 2026
3 of 4 checks passed
@jakebodea jakebodea deleted the fix/nav-indicator-scroll branch February 8, 2026 03:44
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before Autofix could start.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on March 11

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

if (activeLink) {
setIndicator({ left: activeLink.offsetLeft, width: activeLink.offsetWidth, opacity: 1 })
}
}, [activeIndex])
Copy link

Choose a reason for hiding this comment

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

Indicator persists when no nav item is active

Low Severity

When activeIndex is -1 (e.g., on a 404 page or a route not in navItems), the useLayoutEffect returns early without updating the indicator state. If the indicator was previously visible (opacity: 1), it remains highlighted on the last active tab. The old layoutId approach correctly removed the indicator by not rendering the motion.span at all. The early return needs to also set opacity: 0 to hide the indicator.

Fix in Cursor Fix in Web

if (activeLink) {
setIndicator({ left: activeLink.offsetLeft, width: activeLink.offsetWidth, opacity: 1 })
}
}, [activeIndex])
Copy link

Choose a reason for hiding this comment

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

Indicator position stale after font swap or resize

Low Severity

The useLayoutEffect only re-measures when activeIndex changes. Since the site uses Google Fonts with display: "swap", the initial measurement may use fallback font metrics. When the custom font (Montserrat) loads and text reflows, offsetLeft/offsetWidth values change but the effect doesn't re-run, leaving the indicator misaligned. The previous layoutId approach handled layout changes automatically.

Fix in Cursor Fix in Web

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