Tab layout: no side shade on phones; a disabled tab looks disabled

- The closed phone drawer casts no shadow. Parked off-screen at
  translateX(-100%), its 28px blur still darkened the start edge of every
  map (measured at 390: the left 30px about 22 luminance levels darker
  than with the shadow off, a repeat shot identical). The shadow now
  belongs to the open drawer only; in Arabic the same rule covers the
  drawer parked off the right edge.
- A row with disabled (a desktop-only map on a phone, e.g. Big Picture)
  renders greyed, with aria-disabled and a not-allowed cursor; its badge
  keeps its accent colour. pick() is unchanged, so the consumer still
  receives the click and can say why the row does not open.
Both CSS copies change identically.
This commit is contained in:
Doxa Maps 2026-09-30 07:04:01 +00:00
parent 86a9ee66c1
commit a88ae76aeb

View file

@ -186,8 +186,9 @@
<div class="vtl-rows" v-show="!grp.folder || !isFolderClosed(grp)">
<button
v-for="row in grp.rows" :key="row.key" type="button" role="tab"
class="vtl-tab" :class="{ active: row.key === active }"
class="vtl-tab" :class="{ active: row.key === active, disabled: row.disabled }"
:aria-selected="String(row.key === active)"
:aria-disabled="row.disabled ? 'true' : null"
:tabindex="row.key === active ? 0 : -1"
:ref="el => registerTab(row.key, el)"
@click="pick(row.key)" @keydown="onKey($event)">
@ -210,8 +211,9 @@
<template v-else>
<button
v-for="row in items" :key="row.key" type="button" role="tab"
class="vtl-tab" :class="{ active: row.key === active }"
class="vtl-tab" :class="{ active: row.key === active, disabled: row.disabled }"
:aria-selected="String(row.key === active)"
:aria-disabled="row.disabled ? 'true' : null"
:tabindex="row.key === active ? 0 : -1"
:ref="el => registerTab(row.key, el)"
@click="pick(row.key)" @keydown="onKey($event)">
@ -402,6 +404,10 @@ const VTL_SHADOW_CSS = `
.vtl-tab:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-tab:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.active { background:var(--vtl-accented); color:#3b463d; font-weight:500; }
/* A DISABLED ROW (row.disabled, e.g. a desktop-only map on a phone) stays in the rail, greyed, with its badge
still readable. The click still reaches the consumer, so it can say WHY the row does not open. */
.vtl-tab.disabled, .vtl-tab.disabled:hover { background:none; color:#a3a8a4; cursor:not-allowed; }
.vtl-tab.disabled .vtl-tab-ic, .vtl-tab.disabled .vtl-tab-emoji, .vtl-tab.disabled .vtl-tab-fallback { opacity:.45; }
.vtl-tab.active .vtl-tab-ic { color:var(--vtl-accent); }
.vtl-tab-ic { flex:0 0 auto; display:inline-flex; color:var(--vtl-muted); }
.vtl-tab-ic svg { width:20px; height:20px; }
@ -505,13 +511,15 @@ const VTL_SHADOW_CSS = `
/* MOBILE = the SAME composited-transform model, taken to its full-off-canvas extreme: a full-width
drawer that slides in/out via transform (never a layout property), so opening/closing the drawer
never reflows the content. Content is full-width (left:0) and stays put; the drawer floats over it. */
.vtl-nav { z-index:20; width:256px; box-shadow:0 0 28px rgba(0,0,0,.55); transform:translateX(-100%); }
/* NO SHADOW WHILE CLOSED: parked at translateX(-100%), a 28px blur still bleeds into the screen along the start
edge (measured at 390: the left 30px ~22 luminance levels darker). The shadow belongs to the OPEN drawer only. */
.vtl-nav { z-index:20; width:256px; box-shadow:none; transform:translateX(-100%); }
/* OPEN DRAWER ABOVE THE PLUGIN'S OWN LAYERS. Mounted content shares this stacking context, and
plugins raise their chrome (the research dashboard's filter bar is z-index:55), so at z-index 20
the open drawer had a plugin's filter row painted across it — measured 2026-09-25 at 390px.
Only while OPEN on a phone: that is when the drawer is the one thing you can use, and desktop
and plugin modal scrims keep their existing order. */
.vtl.nav-open .vtl-nav { transform:translateX(0); z-index:1000; }
.vtl.nav-open .vtl-nav { transform:translateX(0); z-index:1000; box-shadow:0 0 28px rgba(0,0,0,.55); }
.vtl-main { left:0; }
/* The desktop slim-rail collapse has no meaning on a phone — reconcileForWidth() clears railCollapsed
at this width, but neutralize its CSS too (belt-and-suspenders) so a full labelled drawer always
@ -968,6 +976,10 @@ export default defineComponent({
.vtl-tab:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-tab:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.active { background:var(--vtl-accented); color:#3b463d; font-weight:500; }
/* A DISABLED ROW (row.disabled, e.g. a desktop-only map on a phone) stays in the rail, greyed, with its badge
still readable. The click still reaches the consumer, so it can say WHY the row does not open. */
.vtl-tab.disabled, .vtl-tab.disabled:hover { background:none; color:#a3a8a4; cursor:not-allowed; }
.vtl-tab.disabled .vtl-tab-ic, .vtl-tab.disabled .vtl-tab-emoji, .vtl-tab.disabled .vtl-tab-fallback { opacity:.45; }
.vtl-tab.active .vtl-tab-ic { color:var(--vtl-accent); }
.vtl-tab-ic { flex:0 0 auto; display:inline-flex; color:var(--vtl-muted); }
.vtl-tab-ic svg { width:20px; height:20px; }
@ -1071,13 +1083,15 @@ export default defineComponent({
/* MOBILE = the SAME composited-transform model, taken to its full-off-canvas extreme: a full-width
drawer that slides in/out via transform (never a layout property), so opening/closing the drawer
never reflows the content. Content is full-width (left:0) and stays put; the drawer floats over it. */
.vtl-nav { z-index:20; width:256px; box-shadow:0 0 28px rgba(0,0,0,.55); transform:translateX(-100%); }
/* NO SHADOW WHILE CLOSED: parked at translateX(-100%), a 28px blur still bleeds into the screen along the start
edge (measured at 390: the left 30px ~22 luminance levels darker). The shadow belongs to the OPEN drawer only. */
.vtl-nav { z-index:20; width:256px; box-shadow:none; transform:translateX(-100%); }
/* OPEN DRAWER ABOVE THE PLUGIN'S OWN LAYERS. Mounted content shares this stacking context, and
plugins raise their chrome (the research dashboard's filter bar is z-index:55), so at z-index 20
the open drawer had a plugin's filter row painted across it — measured 2026-09-25 at 390px.
Only while OPEN on a phone: that is when the drawer is the one thing you can use, and desktop
and plugin modal scrims keep their existing order. */
.vtl.nav-open .vtl-nav { transform:translateX(0); z-index:1000; }
.vtl.nav-open .vtl-nav { transform:translateX(0); z-index:1000; box-shadow:0 0 28px rgba(0,0,0,.55); }
.vtl-main { left:0; }
/* The desktop slim-rail collapse has no meaning on a phone — reconcileForWidth() clears railCollapsed
at this width, but neutralize its CSS too (belt-and-suspenders) so a full labelled drawer always