explicit smooth-scroll handlers on Navbar links

Vue Router's scrollBehavior doesn't fire when RouterLink resolves to
the current URL (logo while on /, /#bundles while already at #bundles),
so the smooth scroll never happened. Added onNavClick + onLogoClick
handlers that intercept the click when the destination is the same
route and call scrollIntoView / window.scrollTo with behavior:'smooth'
directly. Cross-route nav still goes through Vue Router's scrollBehavior
unchanged (savedPosition restore + same-route smooth + cross-route
instant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-05-03 16:17:47 +01:00
parent eefaa06995
commit 7cad9d27c8
14 changed files with 50 additions and 16 deletions

View File

@@ -148,6 +148,39 @@ function isActive(href) {
return href === hereHash
}
// RouterLink doesn't fire a navigation when the destination resolves
// to the current URL, so Vue Router's scrollBehavior never runs for
// "logo while on /" or "/#bundles while on /#bundles". We handle the
// scroll manually here so those clicks always animate.
function onNavClick(item, event) {
if (!item || !item.href) return
const [path, hash] = item.href.split('#')
const targetPath = path || '/'
const onSameRoute = targetPath === route.path
if (!onSameRoute) return
if (hash) {
const el = document.getElementById(hash)
if (el) {
event?.preventDefault?.()
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
try {
history.replaceState(history.state, '', `#${hash}`)
} catch {}
}
return
}
// Same path, no hash (logo click on home) → smooth-scroll to top.
event?.preventDefault?.()
window.scrollTo({ top: 0, behavior: 'smooth' })
}
function onLogoClick(event) {
if (route.path === '/') {
event?.preventDefault?.()
window.scrollTo({ top: 0, behavior: 'smooth' })
}
}
watch(menuOpen, (open) => {
if (typeof document === 'undefined') return
document.documentElement.style.overflow = open ? 'hidden' : ''
@@ -167,6 +200,7 @@ onBeforeUnmount(() => {
to="/"
:class="['block shrink-0 py-1', tone.logo]"
aria-label="Kaiser Natron home"
@click="onLogoClick"
>
<Logo :class="logoClasses" />
</RouterLink>
@@ -179,7 +213,7 @@ onBeforeUnmount(() => {
isActive(item.href) ? 'text-accent' : tone.link,
'text-[14px] font-medium tracking-label transition-colors duration-base',
]"
@click="$emit('nav', item)"
@click="onNavClick(item, $event); $emit('nav', item)"
>{{ itemLabel(item) }}</RouterLink>
</nav>
</div>
@@ -202,7 +236,7 @@ onBeforeUnmount(() => {
isActive(item.href) ? 'text-accent' : tone.link,
'text-[14px] font-medium tracking-label transition-colors duration-base',
]"
@click="$emit('nav', item)"
@click="onNavClick(item, $event); $emit('nav', item)"
>{{ itemLabel(item) }}</RouterLink>
</nav>
<button
@@ -310,7 +344,7 @@ onBeforeUnmount(() => {
? 'text-accent'
: 'text-cream hover:text-accent',
]"
@click="menuOpen = false; $emit('nav', item)"
@click="onNavClick(item, $event); menuOpen = false; $emit('nav', item)"
>{{ itemLabel(item) }}</RouterLink>
</nav>