fix: anchor scroll — double-rAF timing + scroll-mt-20 on match cards

useEffect fired before the browser had laid out the freshly-rendered
match cards, so getElementById returned null or scrollIntoView ran
before the element was in its final position. Double requestAnimationFrame
waits for React's commit AND the browser's layout pass.

scroll-mt-20 (80 px) adds clearance for the 60 px sticky nav so the
targeted card isn't hidden beneath it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 01:52:20 +02:00
parent 1fc9c59367
commit c721062560
+7 -4
View File
@@ -76,8 +76,11 @@ export function TournamentClient({ params }: { params: Promise<{ year: string }>
if (!data) return if (!data) return
const hash = window.location.hash const hash = window.location.hash
if (!hash) return if (!hash) return
// double-rAF: first frame commits React's DOM, second frame lets the browser lay out
requestAnimationFrame(() => requestAnimationFrame(() => {
const el = document.getElementById(hash.slice(1)) const el = document.getElementById(hash.slice(1))
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' }) if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' })
}))
}, [data]) }, [data])
@@ -162,7 +165,7 @@ export function TournamentClient({ params }: { params: Promise<{ year: string }>
<h2 className="font-['Bebas_Neue'] text-2xl text-green-light mb-4">LIVE</h2> <h2 className="font-['Bebas_Neue'] text-2xl text-green-light mb-4">LIVE</h2>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
{liveMatches.map(m => ( {liveMatches.map(m => (
<div key={m.id} id={`match-${m.id}`}> <div key={m.id} id={`match-${m.id}`} className="scroll-mt-20">
<MatchCard match={m} /> <MatchCard match={m} />
<GoalList match={m} /> <GoalList match={m} />
</div> </div>
@@ -205,7 +208,7 @@ export function TournamentClient({ params }: { params: Promise<{ year: string }>
{/* Group matches */} {/* Group matches */}
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{groupMatches.map(m => ( {groupMatches.map(m => (
<div key={m.id} id={`match-${m.id}`}> <div key={m.id} id={`match-${m.id}`} className="scroll-mt-20">
<MatchCard match={m} compact /> <MatchCard match={m} compact />
<GoalList match={m} /> <GoalList match={m} />
</div> </div>
@@ -226,7 +229,7 @@ export function TournamentClient({ params }: { params: Promise<{ year: string }>
<h3 className="text-[13px] font-bold text-green tracking-wide uppercase mb-3">{round}</h3> <h3 className="text-[13px] font-bold text-green tracking-wide uppercase mb-3">{round}</h3>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
{roundMatches.map(m => ( {roundMatches.map(m => (
<div key={m.id} id={`match-${m.id}`}> <div key={m.id} id={`match-${m.id}`} className="scroll-mt-20">
<MatchCard match={m} compact /> <MatchCard match={m} compact />
<GoalList match={m} /> <GoalList match={m} />
</div> </div>