From dade6c3cb40278d3a130558adeb6795a9844e197 Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Sat, 11 Jul 2026 01:10:15 -0700 Subject: [PATCH] Restyle location-picker map: themed CARTO basemap + branded pin Replace the plain gray OpenStreetMap raster in the location picker with a theme-aware CARTO basemap (no API key): sleek "dark matter" tiles under the dark theme, colorful "voyager" tiles under the light theme, so the map matches whichever is active. Swap Leaflet's default blue marker for an accent-colored teardrop pin (the same glyph the Find button uses), and theme the zoom controls + attribution to sit in the app's surfaces. - mappicker.js: theme-aware CARTO tileLayer (retina, CARTO attribution); L.divIcon "mp-pin" used for the marker - style.css: .mp-pin styling, themed .leaflet-bar / attribution, map shadow Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01B3Q2EkHHnTUX2BfhV5q1Zc --- static/mappicker.js | 23 ++++++++++++++++++----- static/style.css | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/static/mappicker.js b/static/mappicker.js index edb17ee..0cab3e5 100644 --- a/static/mappicker.js +++ b/static/mappicker.js @@ -9,7 +9,7 @@ // itself is created lazily the first time the picker opens (and reused after), so // pages that never open it pay nothing. (function () { - let overlay, mapEl, map, marker, pin = null, onPickCb = null; + let overlay, mapEl, map, marker, pin = null, onPickCb = null, pinIcon = null; let searchForm, searchInput, sugEl, confirmBtn, hintEl; const PIN_ICON = ``; @@ -93,7 +93,7 @@ function setPin(lat, lon, zoom) { pin = { lat, lon }; if (marker) marker.setLatLng([lat, lon]); - else marker = L.marker([lat, lon]).addTo(map); + else marker = L.marker([lat, lon], pinIcon ? { icon: pinIcon } : undefined).addTo(map); map.setView([lat, lon], zoom || map.getZoom()); confirmBtn.disabled = false; hintEl.textContent = "Pin set — confirm below, or tap elsewhere to move it."; @@ -117,10 +117,23 @@ overlay.hidden = false; if (!map) { - map = L.map(mapEl, { worldCopyJump: true }).setView([44.5, -95], 4); - L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { - maxZoom: 18, attribution: "© OpenStreetMap contributors", + map = L.map(mapEl, { worldCopyJump: true, zoomControl: true }).setView([44.5, -95], 4); + // Themed CARTO basemap (no key needed) instead of the plain gray OSM raster: + // sleek "dark matter" tiles under the app's dark theme, colorful "voyager" + // tiles under the light theme, so the picker matches whichever is active. + const dark = matchMedia("(prefers-color-scheme: dark)").matches; + const tiles = dark + ? "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png" + : "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"; + L.tileLayer(tiles, { + subdomains: "abcd", maxZoom: 20, detectRetina: true, + attribution: '© OpenStreetMap © CARTO', }).addTo(map); + // Branded accent teardrop pin (the same glyph the Find button uses), styled + // in style.css — replaces Leaflet's default blue raster marker. + pinIcon = L.divIcon({ + className: "mp-pin", html: PIN_ICON, iconSize: [32, 32], iconAnchor: [16, 30], + }); map.on("click", (e) => setPin(e.latlng.lat, e.latlng.lng)); } // Leaflet measures the container on creation; it's zero-sized while hidden, so diff --git a/static/style.css b/static/style.css index cc8ebfe..d1e1281 100644 --- a/static/style.css +++ b/static/style.css @@ -680,7 +680,27 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; } .mp-map { flex: 1 1 auto; min-height: 300px; margin: 4px 16px; z-index: 0; border-radius: 12px; overflow: hidden; border: 1px solid var(--border); + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25), 0 6px 18px rgba(0, 0, 0, .25); } +.mp-map .leaflet-container { background: var(--surface-2); } +/* Branded accent teardrop pin (an L.divIcon wrapping the Find-button glyph), + replacing Leaflet's default blue raster marker. */ +.mp-pin { background: none; border: none; filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .45)); } +.mp-pin svg { width: 32px; height: 32px; display: block; } +.mp-pin path { fill: var(--accent); stroke: rgba(0, 0, 0, .35); stroke-width: 1.25; } +.mp-pin circle { fill: #fff; stroke: none; } +/* Theme the Leaflet zoom buttons + attribution to sit in the app's surfaces + instead of Leaflet's stock white chrome. */ +.mp-map .leaflet-bar { border: none; box-shadow: 0 2px 8px rgba(0, 0, 0, .3); } +.mp-map .leaflet-bar a { + background: var(--surface); color: var(--text); border-bottom-color: var(--border); +} +.mp-map .leaflet-bar a:hover { background: var(--surface-2); color: var(--accent); } +.mp-map .leaflet-control-attribution { + background: color-mix(in srgb, var(--surface) 80%, transparent); + color: var(--muted); backdrop-filter: blur(2px); border-top-left-radius: 6px; +} +.mp-map .leaflet-control-attribution a { color: var(--accent); } .mp-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px 16px; flex-wrap: wrap;