From aa05a67e6105c2fff70d6a0afbd5a71c4bdc3abd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 26 May 2026 04:07:26 +0000 Subject: [PATCH] PageTransition: restore scroll-to-top with behavior: 'instant' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-adds the post-navigation scroll reset that was dropped in the softer-transition rework. The previous version was vulnerable to a race anyway: globals.css sets scroll-behavior: smooth on for in-page anchor scrolling, and the default scrollTo form inherits that — so the cross-page jump turned into a tween that the AnimatePresence mode="popLayout" layout shift could clamp partway up, landing users on a long account page partway up the new (shorter) collection page instead of at the top. Fix: pass behavior: 'instant' explicitly so the navigation jump always runs synchronously, regardless of CSS. Hash navigations still skip the reset so in-page anchors keep working. --- src/components/PageTransition.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/PageTransition.tsx b/src/components/PageTransition.tsx index e7c3f02..7d9e862 100644 --- a/src/components/PageTransition.tsx +++ b/src/components/PageTransition.tsx @@ -2,7 +2,7 @@ import { motion, AnimatePresence, MotionConfig } from 'framer-motion'; import { usePathname } from 'next/navigation'; -import { ReactNode } from 'react'; +import { ReactNode, useEffect } from 'react'; interface PageTransitionProps { children: ReactNode; @@ -31,6 +31,23 @@ interface PageTransitionProps { export default function PageTransition({ children }: PageTransitionProps) { const pathname = usePathname(); + // Reset scroll on route change. Next.js does this automatically, but + // AnimatePresence mode="popLayout" keeps the outgoing page mounted + // during the crossfade, which races the built-in scroll restoration + // and leaves the new page scrolled to wherever the old one was. Hash + // navigations are passed through so in-page anchors still work. + // + // `behavior: 'instant'` is required: globals.css sets + // `scroll-behavior: smooth` on for in-page anchor scrolling, + // and the default form would inherit that — turning the scroll-to-top + // into an animated tween that races the page-transition layout shift + // and lands the user partway up the new page instead of at 0. + useEffect(() => { + if (typeof window === 'undefined') return; + if (window.location.hash) return; + window.scrollTo({ top: 0, left: 0, behavior: 'instant' }); + }, [pathname]); + return ( -- 2.51.2