fix(ui): unblock the zoom control, tidy Go Pro, and make the route map bigger

Three phone issues from a screenshot review:

- Map: the floating toolbar sat on top of Leaflet's top-left zoom control.
  index.css now offsets .leaflet-top.leaflet-left by 4.25rem under max-width
  767px, scoped to the map page via a new  class
  (desktop toolbar is in flow, so no offset). Verified at 390x844.
- Sasquatch: the cramped red 8px 'Go Pro' chip is now a proper lime pill with
  the bolt, matching the panel.
- Directions: the map was 38vh and buried under the mode row. It's now 50vh
  (440px on larger screens; 60vh/62vh while following) and sits directly under
  the header, with the mode/vehicle row and 'From' row moved BELOW it. Done with
  flex order-* utilities rather than moving JSX; Start bar stays pinned.
This commit is contained in:
SkateSpots 2026-09-29 17:30:25 +07:00
parent 1c7bd2aa6e
commit 1e0902f6b9
5 changed files with 39 additions and 19 deletions

View file

@ -295,13 +295,16 @@ session with **no other memory of this project**. So:
**Still deliberately absent: auto-reroute if you leave the route.** True
screen-off/background navigation needs a native foreground service — the app
must stay foregrounded to guide.
- **Dialog layout (2026-09-27):** app-like flex column —
`flex max-h-[calc(100dvh-2rem)] flex-col overflow-hidden`: header, mode row,
"From" row and the **map** are `shrink-0` (map `h-[38vh] sm:h-[400px]`, or
`h-[58vh]`/`sm:h-[60vh]` while following), the
middle (stat tiles + steps) is the only scroll region (`min-h-0 flex-1
overflow-y-auto`), and the **Start/Stop bar is pinned** at the bottom so it's
always reachable. ⚠️ Don't go back to a single `overflow-hidden` content box:
- **Dialog layout (2026-09-29):** app-like flex column —
`flex max-h-[calc(100dvh-2rem)] flex-col overflow-hidden`. **Visual order is
set with flex `order-*` utilities, not DOM order** (avoids moving JSX): the
**map** is `order-2` (directly under the header — it's the point of the
screen), the **mode/vehicle row** `order-3` and **"From" row** `order-4` sit
*below* the map, the details are `order-5`, and the **Start/Stop bar** is
`order-6`, pinned at the bottom so it's always reachable. The map is
`shrink-0` at `h-[50vh] min-h-[300px] sm:h-[440px]` (was `38vh`), or
`h-[60vh]`/`sm:h-[62vh]` while following. ⚠️ Don't go back to a single
`overflow-hidden` content box:
that made everything below the fold **unreachable** once the map grew. The
summary is three `StatTile`s (distance / time / arrive) + a detail line
(steps, driving-roads note, avg speed) and the map carries a metric
@ -649,6 +652,11 @@ session with **no other memory of this project**. So:
re-add a stacked toolbar or the footer subtraction. The "New here?" welcome
banner (logged-out only) still occupies ~69px *in flow* on first visit — it's
dismissible and the dismissal is persisted (`skatespots_welcome_dismissed`).
⚠️ The floating toolbar sits over Leaflet's default **top-left zoom control**,
so `MapPage` passes `className="h-full ss-map-floating-toolbar"` and
`index.css` offsets `.leaflet-top.leaflet-left` by `4.25rem` **only under
`max-width:767px`** (desktop toolbar is in flow, no offset). Verified at
390×844: zoom top 135px vs search bottom 108px.
- **Empty-map nudge (2026-09-28):** `MapPage` distinguishes the two empties — the
existing "No spots match" card is for *filters* (`filteredSpots.length === 0 &&
allLocations.length > 0`) and a new "No spots mapped yet — be the first" card

View file

@ -668,10 +668,12 @@ export function RouteDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
{/* App-like shell: header + modes + map are pinned, the details in the
middle scroll, and the Start/Stop bar stays reachable at the bottom. */}
{/* App-like shell. VISUAL ORDER is set with flex `order` utilities, not DOM
order: the map sits directly under the header (bigger — it's the point
of the screen), the mode/vehicle row moves BELOW it, and the details
scroll with the Start/Stop bar pinned at the bottom. */}
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-lg flex-col gap-0 overflow-hidden p-0 sm:max-w-xl">
<DialogHeader className="shrink-0 p-4 pb-3">
<DialogHeader className="order-1 shrink-0 p-4 pb-3">
<DialogTitle className="flex items-center gap-2 text-lg">
<Navigation className="h-5 w-5 text-[#ccff00]" />
Route to {destinationLabel}
@ -683,8 +685,8 @@ export function RouteDialog({
</DialogDescription>
</DialogHeader>
{/* Mode toggle */}
<div className="flex shrink-0 gap-1.5 px-4 pb-3">
{/* Mode toggle (vehicles) — sits BELOW the map via order-3. */}
<div className="order-3 flex shrink-0 gap-1.5 px-4 pb-3">
{TRAVEL_MODES.map((m) => (
<button
key={m.mode}
@ -705,7 +707,7 @@ export function RouteDialog({
</div>
{/* Start point — default is where you are; you can pick another on the map */}
<div className="flex shrink-0 flex-wrap items-center gap-1.5 px-4 pb-3">
<div className="order-4 flex shrink-0 flex-wrap items-center gap-1.5 px-4 pb-3">
<span className="text-[10px] font-black uppercase tracking-widest text-muted-foreground">
From
</span>
@ -743,7 +745,7 @@ export function RouteDialog({
`.leaflet-container { height: 100% }` override beats a `h-64` on the
map element itself, and against a height-less parent that resolved to
0 — a fully invisible map with no route line. */}
<div className={cn('relative shrink-0', following ? 'h-[58vh] min-h-[340px] sm:h-[60vh]' : 'h-[38vh] min-h-[220px] sm:h-[400px]')}>
<div className={cn('order-2 relative shrink-0', following ? 'h-[60vh] min-h-[360px] sm:h-[62vh]' : 'h-[50vh] min-h-[300px] sm:h-[440px]')}>
<div
ref={setMapDiv}
className="h-full w-full bg-muted"
@ -841,7 +843,7 @@ export function RouteDialog({
</div>
{/* Scrollable details */}
<div className="min-h-0 flex-1 overflow-y-auto">
<div className="order-5 min-h-0 flex-1 overflow-y-auto">
{/* Summary bar */}
<div className="p-4 border-t bg-muted/30 min-h-[64px] flex items-center">
@ -978,7 +980,7 @@ export function RouteDialog({
{/* Follow mode: live turns + voice, with the screen kept awake. */}
{route && !route.transit && (
<div className="flex items-center gap-3 border-t bg-muted/30 px-4 py-3">
<div className="order-6 flex items-center gap-3 border-t bg-muted/30 px-4 py-3">
{arrived ? (
<Button
size="sm"

View file

@ -1655,10 +1655,11 @@ Instructions:
) : user ? (
<button
onClick={() => setProDialogOpen(true)}
className="flex items-center gap-0.5 rounded bg-red-500/20 px-1 py-0.5 text-[8px] font-black uppercase tracking-wider text-red-400 hover:bg-red-500/30 transition-colors"
className="ml-0.5 inline-flex items-center gap-1 rounded-full border border-[#ccff00]/40 bg-[#ccff00]/10 px-2 py-0.5 text-[9px] font-black uppercase tracking-wider text-[#ccff00] transition-colors hover:border-[#ccff00]/70 hover:bg-[#ccff00]/20"
title="Upgrade to Sasquatch Pro"
>
⚡ Go Pro
<span aria-hidden>⚡</span>
Go Pro
</button>
) : null}
</div>

View file

@ -122,6 +122,15 @@
padding-bottom: 64px;
}
/* The map page's toolbar floats over the top of the map on mobile, which would
otherwise sit on top of Leaflet's default top-left zoom control. Push that
control clear of it (desktop keeps the toolbar in flow, so no offset). */
@media (max-width: 767px) {
.ss-map-floating-toolbar .leaflet-top.leaflet-left {
margin-top: 4.25rem;
}
}
.leaflet-popup-content-wrapper {
border-radius: calc(var(--radius) - 4px);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

View file

@ -1633,7 +1633,7 @@ export default function MapPage() {
onMapClick={handleMapClick}
onMapMove={setMapCenter}
enableClickToAdd={!!user}
className="h-full"
className="h-full ss-map-floating-toolbar"
weatherEnabled={weatherEnabled}
weatherData={weatherData}
events={upcomingEvents || []}