Phones: the gallery's ☰ moves into a bar above the pane, off every map

The floating ☰ circle sat in the bottom-left corner of whatever map was open.
It covered Simple Research's search box and the licence credit on Cities and
Gospel Resources (in Arabic, mirrored, the credit's other end). On a phone
(narrow and headerless, the circle's own condition) the ☰ now sits in a 44px
bar above the pane, beside the open view's name. The layout no longer
publishes a fab clearance, so the maps' bottom corners return to their plain
insets. Desktop and header-on layouts are unchanged.
This commit is contained in:
Doxa Maps 2026-09-30 03:07:19 +00:00
parent 8133f28b6b
commit d83c89f287

View file

@ -103,7 +103,7 @@
sit NEXT TO the circle rather than on top of it (12px inset + 44px button + 8px gap). A
consumer that hardcoded 64px would silently overlap the day the circle resizes; reading it
means the circle's geometry is stated once, here, by the component that owns it. -->
<div class="vtl" :class="{ 'nav-open': navOpen, 'rail-collapsed': railCollapsed, 'no-header': !header }"
<div class="vtl" :class="{ 'nav-open': navOpen, 'rail-collapsed': railCollapsed, 'no-header': !header, 'has-bar': barShown }"
:style="fabShown ? { '--map-fab-clearance': 'calc(56px + env(safe-area-inset-bottom))', '--map-fab-beside': 'calc(64px + env(safe-area-inset-left))' } : null">
<header class="vtl-top" v-if="header">
<button class="vtl-menu" type="button" :aria-expanded="String(navOpen)" @click="navOpen = !navOpen"
@ -123,11 +123,18 @@
Rendered ONLY when narrow AND headerless, so no existing consumer can see a change: every
other case is desktop (rail always visible) or header-on (the toggle above already works).
It hides itself while the drawer is open — tapping a tab closes the drawer anyway, and a
floating button over an open drawer would just cover a row. -->
<button v-if="fabShown"
class="vtl-fab" type="button" :aria-expanded="String(navOpen)"
@click="navOpen = true" :aria-label="label('openViews')">☰</button>
★ THE PHONE BAR REPLACED THE FLOATING CIRCLE (mobile pass, 2026-09-30). The circle floated over
whatever each map put in its bottom-left corner: Simple Research's search box, and the licence
credit on Cities and Gospel Resources (measured at 390, en and ar). No placement of a floating
button is free on every map, so on a phone the ☰ lives in a 44px bar ABOVE the pane, with the
open view's name beside it, and covers nothing. It stays while the drawer is open: its ☰
closes the drawer again. The button keeps the vtl-menu class, which is how
check:nav-reachable-mobile finds the rail's opener. -->
<div v-if="barShown" class="vtl-bar">
<button class="vtl-menu vtl-bar-menu" type="button" :aria-expanded="String(navOpen)"
@click="navOpen = !navOpen" :aria-label="label('openViews')">☰</button>
<span class="vtl-bar-title">{{ activeLabel }}</span>
</div>
<!-- Beside the circle, driving the shell's own scroller. Reads --map-fab-beside, which this
layout publishes only while the circle is up, so with no circle it falls back to the
@ -315,6 +322,11 @@ const VTL_SHADOW_CSS = `
content to size it and collapses to 0 (the 1fr row sits empty) → the same 0-height black screen.
With no header there is only one region, so declare a single 1fr row and the body fills. */
.vtl.no-header { grid-template-rows:1fr; }
.vtl.has-bar { grid-template-rows:auto 1fr; }
.vtl-bar { display:flex; align-items:center; gap:10px; box-sizing:border-box; height:44px; padding:0 10px; border-bottom:1px solid var(--vtl-border); background:#ffffff; }
.vtl-bar .vtl-bar-menu { display:inline-flex; align-items:center; justify-content:center; flex:none; width:36px; height:36px; padding:0; font:18px/1 system-ui; }
.vtl-bar .vtl-bar-menu:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:2px; }
.vtl-bar-title { flex:1; min-width:0; font-size:15px; font-weight:600; color:#3b463d; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* MATERIAL APP BAR = 64px desktop / 56px mobile (2026-09-21), was 51px from 11px padding.
Stated as an explicit min-height rather than left to padding arithmetic, so the number a
reader sees is the number that renders. min-height keeps a wrapping title from clipping.
@ -625,7 +637,17 @@ export default defineComponent({
* has a symptom a gate can see, so the fix is to make disagreement impossible rather than to
* test for it.
*/
fabShown() { return this.narrow && !this.header && !this.navOpen },
// The circle no longer renders (the phone bar replaced it), so it reserves nothing: every consumer
// that reads --map-fab-clearance / --map-fab-beside falls back to its plain inset.
fabShown() { return false },
// The phone bar: narrow and headerless, like the circle was, and it stays while the drawer is open
// (its ☰ closes the drawer again).
barShown() { return this.narrow && !this.header },
// The open view's name for the bar: the row whose key is `active`, else the layout title.
activeLabel() {
for (const g of this.groups || []) for (const r of (g.rows || [])) if (r.key === this.active) return r.label
return this.title || ''
},
/**
* The element the back-to-top arrow scrolls. .vtl-host is the shell's scroller
* (overflow-y:auto) and there is one PER TAB, so this follows `active` rather than binding a
@ -881,6 +903,11 @@ export default defineComponent({
content to size it and collapses to 0 (the 1fr row sits empty) → the same 0-height black screen.
With no header there is only one region, so declare a single 1fr row and the body fills. */
.vtl.no-header { grid-template-rows:1fr; }
.vtl.has-bar { grid-template-rows:auto 1fr; }
.vtl-bar { display:flex; align-items:center; gap:10px; box-sizing:border-box; height:44px; padding:0 10px; border-bottom:1px solid var(--vtl-border); background:#ffffff; }
.vtl-bar .vtl-bar-menu { display:inline-flex; align-items:center; justify-content:center; flex:none; width:36px; height:36px; padding:0; font:18px/1 system-ui; }
.vtl-bar .vtl-bar-menu:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:2px; }
.vtl-bar-title { flex:1; min-width:0; font-size:15px; font-weight:600; color:#3b463d; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* MATERIAL APP BAR = 64px desktop / 56px mobile (2026-09-21), was 51px from 11px padding.
Stated as an explicit min-height rather than left to padding arithmetic, so the number a
reader sees is the number that renders. min-height keeps a wrapping title from clipping.