fix(spot-card): make the whole card clickable again

The stretched Link was z-0 behind the card content, and backdrop-blur made the card its own paint layer above it, so no click ever reached the link (the card looked dead in the map list view). Link -> z-10, drop backdrop-blur, move hover to the wrapper (group + group-hover:), and raise the action row to z-20 so its buttons stay above the link. Verified headlessly: card click navigates, kebab opens its menu without navigating.
This commit is contained in:
SkateSpots 2026-10-12 14:03:16 +07:00
parent 54882285b6
commit ca810326f3
2 changed files with 15 additions and 7 deletions

View file

@ -1445,6 +1445,14 @@ session with **no other memory of this project**. So:
also add `min-w-0` to truncating flex/grid children. `/stats` also gained a also add `min-w-0` to truncating flex/grid children. `/stats` also gained a
**Top Shops** leaderboard (`topShops` in `useGrowthStats`; `PERSIST_BUSTER` → v6) **Top Shops** leaderboard (`topShops` in `useGrowthStats`; `PERSIST_BUSTER` → v6)
alongside Top Mappers (spots). alongside Top Mappers (spots).
- **Stretched-link card gotcha (2026-10-12):** `SpotCard` renders its whole-card
navigation as an absolutely-positioned `<Link className="absolute inset-0">`
behind the content. It **must** be `z-10` (not `z-0`), and the card must not carry
`backdrop-blur` — a `z-0` link under the content, with `backdrop-blur` making the
card its own paint layer, meant **no click ever reached the link and the card
looked dead**. Hover moved to the wrapper (`group` on the wrapper + `group-hover:`
on the card/image), and the card's action row is `z-20` so its buttons stay above
the link. Keep this shape when editing card components.
- **Dropdown menu scroll gotcha (2026-10-08):** the shared `DropdownMenuContent` - **Dropdown menu scroll gotcha (2026-10-08):** the shared `DropdownMenuContent`
(`components/ui/dropdown-menu.tsx`) shipped `overflow-hidden` with **no (`components/ui/dropdown-menu.tsx`) shipped `overflow-hidden` with **no
max-height**, so any menu taller than the viewport was clipped and could not max-height**, so any menu taller than the viewport was clipped and could not

View file

@ -61,16 +61,16 @@ export function SpotCard({ spot, status, community }: SpotCardProps) {
const isGone = status?.status === 'gone'; const isGone = status?.status === 'gone';
return ( return (
<div className="relative"> <div className="group relative">
{/* Stretched link — the whole card navigates, without nesting interactive {/* Stretched link — the whole card navigates, without nesting interactive
elements (buttons, Zap, dropdowns) or dialogs inside an <a>. */} elements (buttons, Zap, dropdowns) or dialogs inside an <a>. */}
<Link to={route} className="absolute inset-0 z-0" aria-label={spot.title} /> <Link to={route} className="absolute inset-0 z-10" aria-label={spot.title} />
<Card className={cn( <Card className={cn(
"group overflow-hidden hover:shadow-2xl transition-all duration-300 border-2 bg-black/90 backdrop-blur", "overflow-hidden group-hover:shadow-2xl transition-all duration-300 border-2 bg-black/90",
isShop isShop
? "border-[#ccff00]/30 hover:border-[#ccff00]/70 hover:shadow-[#ccff00]/30" ? "border-[#ccff00]/30 group-hover:border-[#ccff00]/70 group-hover:shadow-[#ccff00]/30"
: "border-[#ff0040]/20 hover:border-[#ff0040]/60 hover:shadow-[#ff0040]/30", : "border-[#ff0040]/20 group-hover:border-[#ff0040]/60 group-hover:shadow-[#ff0040]/30",
isGone && "opacity-60 grayscale hover:opacity-90" isGone && "opacity-60 grayscale group-hover:opacity-90"
)}> )}>
{/* Image */} {/* Image */}
<div className="relative aspect-video overflow-hidden bg-black"> <div className="relative aspect-video overflow-hidden bg-black">
@ -214,7 +214,7 @@ export function SpotCard({ spot, status, community }: SpotCardProps) {
)} )}
</div> </div>
<div className="relative z-10 flex items-center gap-2" onClick={(e) => e.preventDefault()}> <div className="relative z-20 flex items-center gap-2" onClick={(e) => e.preventDefault()}>
{user && !isShop && ( {user && !isShop && (
<Button <Button
size="icon" size="icon"