diff --git a/src/index.css b/src/index.css index b2e925b..f5d1c3a 100644 --- a/src/index.css +++ b/src/index.css @@ -395,6 +395,20 @@ } } +/* Indeterminate top loading bar for the map ("Loading spots"). A short segment + sweeps left→right on a faint track, so it reads as active work without + pretending to know real progress. Disabled under reduced-motion. */ +@keyframes ssLoadBar { + 0% { transform: translateX(-120%); } + 100% { transform: translateX(420%); } +} +.ss-loadbar { + animation: ssLoadBar 1.1s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} +@media (prefers-reduced-motion: reduce) { + .ss-loadbar { animation: none; width: 100%; opacity: 0.7; } +} + .animate-fadeIn { animation: fadeIn 0.4s ease-out forwards; } diff --git a/src/pages/MapPage.tsx b/src/pages/MapPage.tsx index 50f0e6f..639c7ef 100644 --- a/src/pages/MapPage.tsx +++ b/src/pages/MapPage.tsx @@ -85,7 +85,6 @@ import { useToast } from '@/hooks/useToast'; import { cn } from '@/lib/utils'; import { getCurrentPosition, GeolocationError, isNativeGeolocation } from '@/lib/geolocation'; import { getLastLocation } from '@/lib/lastLocation'; -import { MapSkeletonPins } from '@/components/MapSkeletonPins'; type ViewMode = 'map' | 'list'; @@ -1615,18 +1614,30 @@ export default function MapPage() { userLocation={userLocation ? { lat: userLocation.lat, lng: userLocation.lon } : null} /> - {/* Loading indicator — slim top edge bar */} + {/* Loading indicator — a slim, honest top bar. The map stays + visible underneath and spots stream in; we NEVER cover the map + with fake placeholder pins. An animated sweep sits on top of a + faint track so it reads as "working", plus a small labeled pill + during a genuine cold start (no markers yet) so the user knows + we're talking to the relays. */} {isLoading && ( -
-
-
+ <> +
+
+
+ {mapSpots.length === 0 && ( +
+
+ + + Loading spots + +
+
+ )} + )} - {/* Cold-start skeleton: only while genuinely loading with NO - markers on the map yet. The instant real (or cached) spots - render, mapSpots.length > 0 and this disappears — no blank map. */} - {isLoading && mapSpots.length === 0 && } - {/* Map attribution — bottom-left, sits ABOVE the mobile bottom