Map tab: full-bleed map with floating glass controls (Apple Weather style)
Publish Container / publish (push) Successful in 2m58s
Publish Container / publish (push) Successful in 2m58s
- Map fills everything between the top bar and bottom nav; no page scroll on this tab - Floating round controls (layers, my location, full screen), readout chips top-left, layers sheet beside the controls, weather summary + radar timeline floating at the bottom - Safe-area aware in full-screen mode; zoom buttons tucked away on touch devices Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4f47d67a7e
commit
8252017417
4 files changed
+102
-60
No files matched your search
+56
-13
@@ -242,16 +242,59 @@ body:not([data-tab="conditions"]) #loading { display: none; }
|
||||
.topbar { margin-left: calc(-1 * (var(--gutter) + env(safe-area-inset-left, 0px))); margin-right: calc(-1 * (var(--gutter) + env(safe-area-inset-right, 0px))); }
|
||||
.install-dialog { margin-top: max(1rem, env(safe-area-inset-top, 0px)); }
|
||||
|
||||
/* ── Map tab: the map is the page (not a 56vh strip); timeline and details sit below it ── */
|
||||
body:not(.map-fs) #tab-unified-map .unified-map-wrap { height: auto; min-height: 0; padding: 0.7rem; gap: 0.6rem; border-radius: 16px; }
|
||||
body:not(.map-fs) #tab-unified-map .unified-map-container {
|
||||
height: max(540px, calc(100dvh - var(--topbar-h) - 250px)); border-radius: 12px;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
body:not(.map-fs) #tab-unified-map .unified-map-container {
|
||||
height: max(460px, calc(100dvh - var(--topbar-h) - 58px - max(6px, env(safe-area-inset-bottom, 0px)) - 190px));
|
||||
}
|
||||
}
|
||||
.map-controls-header { padding-bottom: 0.5rem; gap: 0.5rem; }
|
||||
.map-recenter { font: inherit; font-size: 0.78rem; font-weight: 700; min-height: 40px; padding: 0 0.9rem; border-radius: 999px; border: 1px solid var(--border); background: var(--paper-dark); color: var(--ink); cursor: pointer; margin-left: auto; white-space: nowrap; }
|
||||
.map-recenter:hover { border-color: var(--accent); color: var(--accent); }
|
||||
/* ── Map tab: full-bleed map with floating glass controls (Apple-Weather style) ── */
|
||||
:root { --nav-h: 0px; --hud-top: 12px; --map-safe-b: 0px; --hud-side: 12px; }
|
||||
@media (max-width: 640px) { :root { --nav-h: calc(58px + max(6px, env(safe-area-inset-bottom, 0px))); } }
|
||||
body.map-fs { --hud-top: calc(12px + env(safe-area-inset-top, 0px)); --map-safe-b: env(safe-area-inset-bottom, 0px); }
|
||||
|
||||
.glass { background: color-mix(in srgb, var(--paper-dark) 74%, transparent); backdrop-filter: blur(18px) saturate(1.5); -webkit-backdrop-filter: blur(18px) saturate(1.5);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 65%, transparent); box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
/* The tab fills everything between the top bar and the bottom nav; no page scrolling here */
|
||||
body:not(.map-fs) #tab-unified-map:not(.hidden) { position: fixed; top: var(--topbar-h); left: 0; right: 0; bottom: var(--nav-h); z-index: 60; animation: none; }
|
||||
body[data-tab="unified-map"] { overflow: hidden; }
|
||||
html:has(body[data-tab="unified-map"]) { overflow: hidden; }
|
||||
#tab-unified-map .unified-map-wrap { position: absolute; inset: 0; padding: 0; border: 0; border-radius: 0; background: transparent; display: block; height: auto !important; min-height: 0; }
|
||||
#tab-unified-map .unified-map-shell { position: absolute; inset: 0; }
|
||||
#tab-unified-map .unified-map-container { position: absolute; inset: 0; height: 100% !important; min-height: 0; border: 0; border-radius: 0; }
|
||||
body.map-fs #tab-unified-map { padding: 0; }
|
||||
|
||||
/* Floating controls, top right */
|
||||
.map-hud { position: absolute; top: var(--hud-top); right: max(var(--hud-side), env(safe-area-inset-right, 0px)); display: flex; flex-direction: column; gap: 10px; z-index: 700; }
|
||||
.hud-btn { width: 46px; height: 46px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer; color: var(--ink); font: inherit; font-size: 1.15rem;
|
||||
background: color-mix(in srgb, var(--paper-dark) 78%, transparent); backdrop-filter: blur(18px) saturate(1.5); -webkit-backdrop-filter: blur(18px) saturate(1.5);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 65%, transparent); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); transition: transform .12s, background .15s, color .15s; }
|
||||
.hud-btn:active { transform: scale(0.94); }
|
||||
.hud-btn:hover { color: var(--accent); }
|
||||
.hud-btn.on { background: var(--accent); color: #fff; border-color: var(--accent); }
|
||||
|
||||
/* Readout chips, top left */
|
||||
#tab-unified-map .map-overlay-readouts { position: absolute; top: var(--hud-top); left: max(var(--hud-side), env(safe-area-inset-left, 0px)); right: auto; bottom: auto; flex-direction: column; align-items: flex-start; gap: 8px; z-index: 700; }
|
||||
#tab-unified-map .map-readout-chip { background: color-mix(in srgb, var(--paper-dark) 78%, transparent); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); border: 1px solid color-mix(in srgb, var(--border) 65%, transparent); color: var(--ink); font-weight: 700; padding: 0.4rem 0.8rem; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25); }
|
||||
|
||||
/* Layers sheet: floats beside the controls */
|
||||
#tab-unified-map .map-layer-menu-panel { position: absolute; top: var(--hud-top); right: calc(max(var(--hud-side), env(safe-area-inset-right, 0px)) + 58px); z-index: 710; width: min(330px, calc(100% - 96px)); max-height: calc(100% - var(--hud-top) - 185px);
|
||||
overflow: auto; margin: 0; padding: 0.8rem 0.9rem 0.9rem; border-radius: 20px; }
|
||||
.lm-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.6rem; font-size: 1rem; }
|
||||
.lm-close { width: 34px; height: 34px; border: none; border-radius: 50%; background: var(--paper-darker); color: var(--ink-light); font: inherit; cursor: pointer; }
|
||||
.lm-style { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; font-size: 0.85rem; font-weight: 600; margin-bottom: 0.8rem; }
|
||||
.lm-style select { flex: 1; max-width: 190px; min-height: 40px; font-size: 16px; }
|
||||
#tab-unified-map .map-layer-menu-title { font-size: 0.66rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-light); margin-bottom: 0.4rem; }
|
||||
#tab-unified-map .map-controls-toggle-grid { display: grid; grid-template-columns: 1fr; gap: 2px; }
|
||||
#tab-unified-map .toggle-control { padding: 0.45rem 0.2rem; min-height: 44px; }
|
||||
|
||||
/* Bottom: weather summary + radar timeline float over the map */
|
||||
.map-bottom { position: absolute; left: max(var(--hud-side), env(safe-area-inset-left, 0px)); right: max(var(--hud-side), env(safe-area-inset-right, 0px)); bottom: calc(12px + var(--map-safe-b)); z-index: 650; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
|
||||
.map-bottom > * { pointer-events: auto; }
|
||||
#tab-unified-map .map-timeline-wrap { padding: 0.55rem 0.75rem 0.6rem; border-radius: 18px; border-top: 1px solid color-mix(in srgb, var(--border) 65%, transparent); }
|
||||
#tab-unified-map .map-timeline-wrap.hidden, #tab-unified-map .map-data-panel.hidden { display: none; }
|
||||
#tab-unified-map .map-data-panel { align-self: flex-start; max-width: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 1.1rem; padding: 0.5rem 0.9rem; border-radius: 16px; }
|
||||
#tab-unified-map .panel-location { font-weight: 800; font-size: 0.9rem; }
|
||||
#tab-unified-map .panel-data { display: flex; flex-wrap: wrap; gap: 0.2rem 1rem; }
|
||||
#tab-unified-map .data-item { display: flex; flex-direction: row; align-items: baseline; gap: 0.35rem; font-size: 0.82rem; }
|
||||
@media (max-width: 640px) { #tab-unified-map .data-item:nth-child(2), #tab-unified-map .data-item:nth-child(4), #tab-unified-map .data-item:nth-child(5) { display: none; } }
|
||||
|
||||
/* MapLibre zoom buttons: tucked under the control stack on desktop, hidden on touch */
|
||||
#unified-map .maplibregl-ctrl-top-right { margin-top: calc(var(--hud-top) + 166px); margin-right: max(var(--hud-side), env(safe-area-inset-right, 0px)); }
|
||||
#unified-map .maplibregl-ctrl-top-right .maplibregl-ctrl { margin: 0; }
|
||||
@media (pointer: coarse) { #unified-map .maplibregl-ctrl-top-right { display: none; } }
|
||||
+3
-1
@@ -2489,12 +2489,14 @@ if (baseMapSelect) {
|
||||
}
|
||||
|
||||
const layerMenuToggleBtn = document.getElementById('layer-menu-toggle');
|
||||
document.getElementById('layer-menu-close')?.addEventListener('click', () => { if (!document.getElementById('layer-menu-panel').classList.contains('hidden')) document.getElementById('layer-menu-toggle').click(); });
|
||||
const layerMenuPanel = document.getElementById('layer-menu-panel');
|
||||
if (layerMenuToggleBtn && layerMenuPanel) {
|
||||
layerMenuToggleBtn.addEventListener('click', () => {
|
||||
const willShow = layerMenuPanel.classList.contains('hidden');
|
||||
layerMenuPanel.classList.toggle('hidden', !willShow);
|
||||
layerMenuToggleBtn.textContent = willShow ? 'Hide Layer Menu' : 'Show Layer Menu';
|
||||
if (!layerMenuToggleBtn.classList.contains('hud-btn')) layerMenuToggleBtn.textContent = willShow ? 'Hide Layer Menu' : 'Show Layer Menu';
|
||||
layerMenuToggleBtn.classList.toggle('on', willShow);
|
||||
layerMenuToggleBtn.setAttribute('aria-expanded', willShow ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
|
||||
+42
-43
@@ -248,26 +248,37 @@
|
||||
<div id="station-live" class="sl"></div>
|
||||
</div>
|
||||
|
||||
<!-- ── TAB: MAP (full-bleed map, floating glass controls) ── -->
|
||||
<div id="tab-unified-map" class="tab-panel hidden">
|
||||
<div class="unified-map-wrap">
|
||||
<!-- Map Controls -->
|
||||
<div class="map-controls-header">
|
||||
<div class="map-controls-row">
|
||||
<label for="base-map-select" class="control-label">Base Map:</label>
|
||||
<select id="base-map-select" class="map-select">
|
||||
<div class="unified-map-shell">
|
||||
<div id="unified-map" class="unified-map-container"></div>
|
||||
|
||||
<div class="map-overlay-readouts" id="map-overlay-readouts">
|
||||
<span class="map-readout-chip" id="overlay-temp">Temp --</span>
|
||||
<span class="map-readout-chip" id="overlay-aqi">AQI --</span>
|
||||
<span class="map-readout-chip" id="overlay-sunmoon">Sun -- • Moon --</span>
|
||||
</div>
|
||||
|
||||
<div class="map-hud" role="toolbar" aria-label="Map controls">
|
||||
<button id="layer-menu-toggle" class="hud-btn" type="button" aria-expanded="false" aria-label="Map layers" title="Layers">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M12 3 3 8l9 5 9-5-9-5Z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="m3 12 9 5 9-5M3 16l9 5 9-5" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
<button id="map-recenter-btn" class="hud-btn" type="button" aria-label="My location" title="My location">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M21 3 3 10.5l7 2.5 2.5 7L21 3Z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<button id="map-fullscreen-btn" class="hud-btn radar-fs-btn" type="button" aria-label="Full screen" title="Full screen">⛶</button>
|
||||
</div>
|
||||
|
||||
<div id="layer-menu-panel" class="map-layer-menu-panel glass hidden" role="dialog" aria-label="Map layers">
|
||||
<div class="lm-head"><b>Map</b><button type="button" id="layer-menu-close" class="lm-close" aria-label="Close layers">✕</button></div>
|
||||
<label class="lm-style" for="base-map-select"><span>Style</span> <select id="base-map-select" class="map-select">
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="satellite">Satellite</option>
|
||||
</select>
|
||||
<button id="map-recenter-btn" class="map-recenter" type="button" title="Back to my location">📍 My location</button>
|
||||
<button id="map-fullscreen-btn" class="radar-btn radar-fs-btn" title="Fullscreen">⛶</button>
|
||||
</div>
|
||||
|
||||
<div class="map-layer-menu">
|
||||
<button id="layer-menu-toggle" class="map-layer-menu-toggle" type="button" aria-expanded="false">Show Layer Menu</button>
|
||||
<div id="layer-menu-panel" class="map-layer-menu-panel hidden">
|
||||
<div class="map-layer-menu-title">Map Layers</div>
|
||||
<div class="map-controls-toggle-grid">
|
||||
</select></label>
|
||||
<div class="map-layer-menu-title">Layers</div>
|
||||
<div class="map-controls-toggle-grid">
|
||||
<label class="toggle-control">
|
||||
<span class="toggle-label">📡 Radar</span>
|
||||
<span class="switch-toggle">
|
||||
@@ -346,36 +357,10 @@
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Map -->
|
||||
<div class="unified-map-shell">
|
||||
<div id="unified-map" class="unified-map-container"></div>
|
||||
<div class="map-overlay-readouts" id="map-overlay-readouts">
|
||||
<span class="map-readout-chip" id="overlay-temp">Temp --</span>
|
||||
<span class="map-readout-chip" id="overlay-aqi">AQI --</span>
|
||||
<span class="map-readout-chip" id="overlay-sunmoon">Sun -- • Moon --</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radar/Satellite Timeline Controls -->
|
||||
<div class="map-timeline-wrap hidden" id="timeline-controls">
|
||||
<div class="radar-controls">
|
||||
<button id="timeline-play" class="radar-btn play-btn" title="Play/Pause">▶</button>
|
||||
<div class="radar-timeline" id="timeline-slider"></div>
|
||||
<span id="timeline-timestamp" class="radar-ts">--</span>
|
||||
</div>
|
||||
<div class="radar-legend">
|
||||
<span class="legend-label">Light</span>
|
||||
<div class="legend-bar"></div>
|
||||
<span class="legend-label">Heavy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Panel -->
|
||||
<div class="map-data-panel hidden" id="data-panel">
|
||||
<div class="map-bottom">
|
||||
<div class="map-data-panel glass hidden" id="data-panel">
|
||||
<div class="panel-location">
|
||||
<span id="panel-location-name">--</span>
|
||||
</div>
|
||||
@@ -401,6 +386,20 @@
|
||||
<span id="panel-cloud">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-timeline-wrap glass hidden" id="timeline-controls">
|
||||
<div class="radar-controls">
|
||||
<button id="timeline-play" class="radar-btn play-btn" title="Play/Pause">▶</button>
|
||||
<div class="radar-timeline" id="timeline-slider"></div>
|
||||
<span id="timeline-timestamp" class="radar-ts">--</span>
|
||||
</div>
|
||||
<div class="radar-legend">
|
||||
<span class="legend-label">Light</span>
|
||||
<div class="legend-bar"></div>
|
||||
<span class="legend-label">Heavy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
/* Bottom bars: clear the home indicator and the rounded bottom corners */
|
||||
.bottom-nav { padding-left: max(8px, env(safe-area-inset-left, 0px)); padding-right: max(8px, env(safe-area-inset-right, 0px)); padding-bottom: max(6px, env(safe-area-inset-bottom, 0px)); }
|
||||
|
||||
/* Full-screen map: keep controls off the notch, corners and home indicator */
|
||||
body.map-fs #tab-unified-map { padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px); }
|
||||
body.map-fs .unified-map-wrap { height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); }
|
||||
/* (Full-screen map: the map runs edge to edge; its floating controls add the insets themselves) */
|
||||
|
||||
/* Modal dialogs: stay inside the safe area on short (landscape) screens */
|
||||
dialog { max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 1.5rem); overflow: auto; }
|
||||
Reference in new issue
Block a user