fix(route): overflow-y: visible — hover cards never clip vertically again
The root cause of the hover-clipping we kept band-aiding with larger
track-heights: a scroll container with overflow-x: auto implicitly
clips on the perpendicular axis too. Explicit overflow-y: visible
lets cards expand above and below the track freely.
Implementation matches the spec's belt-and-braces pattern. New layered
markup:
.rr-wrap → position: relative, anchors fades
.rr-scroll → overflow-x: auto + overflow-y: visible,
padding 60/60/80, scroll-padding 60/60 sides
.rr-scroll-inner → structural, no layout effect
.rr-track → positioned at the inner wrapper
The padding-top: 60px / padding-bottom: 80px on .rr-scroll gives cards
room to grow above and below the track without ever hitting a clip
boundary, even on browsers that mis-handle the overflow-x/y mix.
Edge fades reposition: top: 60px / bottom: 80px (was 0 / 16) so they
only cover the track itself, not the overflow padding zones above and
below where hover-expanded cards now live.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b4df8e10f1
commit
83503fe7a3
2 changed files with 20 additions and 9 deletions
|
|
@ -45,7 +45,8 @@
|
||||||
"Bash(pnpm db:seed)",
|
"Bash(pnpm db:seed)",
|
||||||
"Bash(curl -s -c /tmp/jar.txt -d \"email=lars@virk2.dk&password=cab123\" http://localhost:4321/login -o /dev/null -i)",
|
"Bash(curl -s -c /tmp/jar.txt -d \"email=lars@virk2.dk&password=cab123\" http://localhost:4321/login -o /dev/null -i)",
|
||||||
"Bash(curl -s -c /tmp/jar.txt -d \"email=lars@virk2.dk&password=cab123\" http://localhost:4321/login -o /dev/null)",
|
"Bash(curl -s -c /tmp/jar.txt -d \"email=lars@virk2.dk&password=cab123\" http://localhost:4321/login -o /dev/null)",
|
||||||
"Bash(curl -s -b /tmp/jar.txt http://localhost:4321/roadmap)"
|
"Bash(curl -s -b /tmp/jar.txt http://localhost:4321/roadmap)",
|
||||||
|
"Bash(grep -nE \"\\\\.rr-fade-left, \\\\.rr-fade-right|rr-fade-left \\\\{|rr-fade-right \\\\{\" src/components/RoadmapRoute.astro)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,8 @@ const initialShippingX = lastShippingIndex >= 0 ? layout.itemX[lastShippingIndex
|
||||||
<!-- The route — desktop horizontal -->
|
<!-- The route — desktop horizontal -->
|
||||||
<div class="rr-wrap rr-desktop">
|
<div class="rr-wrap rr-desktop">
|
||||||
<div class="rr-scroll" id="rr-scroll">
|
<div class="rr-scroll" id="rr-scroll">
|
||||||
<div class="rr-track" style={`width: ${layout.trackWidth}px; height: 420px;`}>
|
<div class="rr-scroll-inner">
|
||||||
|
<div class="rr-track" style={`width: ${layout.trackWidth}px; height: 420px;`}>
|
||||||
<svg class="rr-path" width={layout.trackWidth} height="420" aria-hidden="true">
|
<svg class="rr-path" width={layout.trackWidth} height="420" aria-hidden="true">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="rr-path-gradient" x1="0" y1="0" x2="1" y2="0">
|
<linearGradient id="rr-path-gradient" x1="0" y1="0" x2="1" y2="0">
|
||||||
|
|
@ -113,6 +114,7 @@ const initialShippingX = lastShippingIndex >= 0 ? layout.itemX[lastShippingIndex
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -239,18 +241,24 @@ const initialShippingX = lastShippingIndex >= 0 ? layout.itemX[lastShippingIndex
|
||||||
/* ── Desktop route ──────────────────────────────────────────────── */
|
/* ── Desktop route ──────────────────────────────────────────────── */
|
||||||
.rr-wrap { position: relative; }
|
.rr-wrap { position: relative; }
|
||||||
.rr-scroll {
|
.rr-scroll {
|
||||||
|
/* overflow-x: auto + overflow-y: visible is the only thing that lets
|
||||||
|
hovered cards expand above/below the track without being clipped.
|
||||||
|
The previous fix bandaged it with extra trackHeight; this is the
|
||||||
|
real fix. The .rr-scroll-inner wrapper is the spec-recommended
|
||||||
|
belt-and-braces in case a browser misbehaves on this combination. */
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
scroll-snap-type: x mandatory;
|
scroll-snap-type: x mandatory;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
/* 60px is enough for a 220px card centred under a dot 60px from
|
/* Top/bottom give cards room to grow above/below the track. The 60px
|
||||||
the edge — card extends 50px past the dot, sits inside the
|
sides give the first/last cards room when fully scrolled. */
|
||||||
padded container. */
|
padding: 60px 60px 80px;
|
||||||
padding: 0 60px 8px;
|
|
||||||
scroll-padding-left: 60px;
|
scroll-padding-left: 60px;
|
||||||
scroll-padding-right: 60px;
|
scroll-padding-right: 60px;
|
||||||
}
|
}
|
||||||
.rr-scroll::-webkit-scrollbar { display: none; }
|
.rr-scroll::-webkit-scrollbar { display: none; }
|
||||||
|
.rr-scroll-inner { /* structural — keeps the track on its own layer */ }
|
||||||
.rr-track { position: relative; }
|
.rr-track { position: relative; }
|
||||||
.rr-path { position: absolute; top: 0; left: 0; pointer-events: none; }
|
.rr-path { position: absolute; top: 0; left: 0; pointer-events: none; }
|
||||||
|
|
||||||
|
|
@ -376,11 +384,13 @@ const initialShippingX = lastShippingIndex >= 0 ? layout.itemX[lastShippingIndex
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Edge fades */
|
/* Edge fades cover only the track itself — the top/bottom padding
|
||||||
|
zones (60/80) on .rr-scroll exist so hover cards can overflow there
|
||||||
|
without clipping, so the fades shouldn't paint over them. */
|
||||||
.rr-fade-left, .rr-fade-right {
|
.rr-fade-left, .rr-fade-right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 60px;
|
||||||
bottom: 16px;
|
bottom: 80px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity .25s ease;
|
transition: opacity .25s ease;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue