Date: March 11, 2026
Complete rebuild of map pin graphics and animation system to match Apple Maps aesthetic with custom enhancements.
Objective: Replace default pins with custom 3D realistic pins inspired by Apple Maps
- Design: 3D sphere effect with radial gradients
- Size: 50x54px
- Features:
- Multi-layer shadows for depth
- Shine/highlight effect (top-left)
- Inner shadow for realism
- Small anchor dot at bottom
- Hover animation (scale 1.15x)
- Design: Inverted teardrop with pointer
- Size: 60x85px
- Features:
- Larger 3D badge circle
- Extended pointer to ground
- Enhanced shadows and gradients
- Icon/image display area
- Prominent anchor dot
Approach: Option C - Combination of lightweight layers and HTML markers
- Mapbox native symbol layers for performance
- Simple circles with icons
- Minimal resource usage
- HTML custom markers for rich visuals
- Swaps SVG content (no marker removal)
- Smooth transitions without position jumping
-
Hover Movement Issue
- Problem: Pins moved away on hover
- Solution: Scale SVG inside container, not container itself
- Transform origin:
center bottom
-
Anchor Point Consistency
- Problem: Visual anchor shifted when zooming/clicking
- Solution:
- Both states use
anchor: 'bottom' - Anchor dots at exact bottom of viewBox
- Default: (25, 51) in 50x54 viewBox
- Active: (30, 81) in 60x85 viewBox
- Both states use
-
Clipped Anchor Dot
- Problem: Dots cut off at viewBox edge
- Solution: Extended viewBox heights to accommodate full circles
- Radial gradients for spherical lighting
- Multi-layer drop shadows (outer glow + depth)
- Shine highlights using ellipse overlays
- Color gradients from light (top) to dark (bottom)
- Transform origin:
center bottomfor natural scaling - Easing:
cubic-bezier(0.4, 0, 0.2, 1)for smooth animations
// Gradient stops
Light: adjustBrightness(color, +40)
Mid: color
Dark: adjustBrightness(color, -30)Library: lucide-react (already installed)
| Type | Icon | Description |
|---|---|---|
art_museum |
Landmark | Columns/monument style |
art_gallery |
Building2 | Building with windows |
monument |
Castle | Historic building |
street_art |
Image | Picture frame |
| Default | Building | Generic building |
- Default pin: 12px icon (scale 0.5)
- Active pin: 18px icon (scale 0.75)
- Positioning: Centered using SVG transform
- Color: Semi-transparent black (default), fillColor (active)
// Always render icon as background
// Attempt to load image if valid path
// Image fades in over icon if successful
// Falls back to icon if image failsImage Path Detection:
- β Skip:
/images/*(known non-existent) - β Allow: HTTP/HTTPS URLs, other local paths
Design: Clean text overlay (no background box)
- Font: Apple system font stack
- Size: 15px, weight 600
- Color: White
- Effect: Multi-layer text-shadow for stroke
text-shadow: /* 4-way black stroke */ -1px -1px 0 rgba(0,0,0,0.8), 1px -1px 0 rgba(0,0,0,0.8), -1px 1px 0 rgba(0,0,0,0.8), 1px 1px 0 rgba(0,0,0,0.8), /* Glow + depth */ 0 0 4px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.4);
- Shows only location name (no category)
- Appears on pin click
- Positioned below anchor dot
- Fade-in animation (200ms)
- Disappears when clicking elsewhere
src/
βββ services/
β βββ mapService.ts # Pin logic, SVG generation
βββ components/
β βββ ui/
β βββ map.tsx # Mapbox map component
β βββ active-pin.tsx # Active pin creator
βββ data/
βββ mockLocations.ts # Location data
| Function | Purpose |
|---|---|
getIconPath() |
Returns SVG paths for location type icons |
getDefaultPinSVG() |
Generates default circle pin SVG |
getActivePinSVG() |
Generates active teardrop pin SVG |
createDefaultPinElement() |
Creates marker DOM element |
createLabelElement() |
Creates label DOM element |
activateMarker() |
Swaps to active state + shows label |
deactivateMarker() |
Swaps to default state + hides label |
addAllMarkers() |
Adds all location markers to map |
activeMarkerState: {
marker: any | null; // Currently active marker
defaultElement: HTMLElement | null;
location: any | null; // Location data
label: HTMLElement | null; // Label element
}- β No external dependencies for graphics
- β Performant and lightweight
- β Scalable at any zoom level
- β Full control over styling
- β Easier to animate complex transitions
- β Can use actual fonts for labels
- β More flexible styling with CSS
- β Better for dynamic content (images)
- β No position jumping
- β Instant transitions
- β Preserves Mapbox anchor point
- β Simpler state management
- β Pins moving away on hover
- β Pins disappearing on click
- β Anchor point shifting when zooming
- β Anchor dot clipped at viewBox edge
- β Icons not displaying properly
- β Icons not showing in active state
- β Images showing broken placeholders
- β Default circular pins with 3D effect
- β Active teardrop pins with pointer
- β Icon system with type-based icons
- β Smart image/icon fallback
- β Active state labels
- β Smooth animations and transitions
- β Consistent anchor points
- β Hover effects
- β³ Animation refinements (can be improved later)
- β³ Real images (when available)
- β³ Additional icon customization options
- Use
anchor: 'bottom'for pins pointing to locations - ViewBox must accommodate all elements (including shadows/dots)
- Transform origin crucial for stable scaling
- Avoid nested SVG elements
- Multi-directional shadows create clean outlines
- Layer shadows (stroke + glow + depth) for best readability
- Works on any background without box
- Default state: Lightweight (50x54px, simple SVG)
- Active state: Rich (60x85px, complex gradients)
- Only one active marker at a time
- Swap content instead of recreating markers
const marker = new mapboxgl.Marker({
element: el,
anchor: 'bottom' // Critical for location accuracy
})
.setLngLat(location.coordinates)
.addTo(map);// Activation
element.innerHTML = getActivePinSVG(location, fillColor, locationType);
element.style.width = '60px';
element.style.height = '85px';
// Deactivation
element.innerHTML = getDefaultPinSVG(fillColor, locationType);
element.style.width = '50px';
element.style.height = '54px';- Add real location images when available
- Implement click-to-zoom functionality
- Add distance/direction indicators
- Create info cards with full location details
- Add routing/directions feature
- Implement location filtering by type
- Add search functionality
- Apple Maps: Pin design and label styling
- Google Maps: Clean text overlays
- 3D Principles: Radial gradients, multi-layer shadows
- Modern UI: Smooth animations, glass morphism hints
Session completed successfully with all requested features implemented and working.