Skip to content

One sidebar, map chrome, phones; the 2026-10-18 review; content-scoped IndexNow - #31

Merged
bdevloed merged 22 commits into
mainfrom
feat/unified-sidebar
Oct 19, 2026
Merged

bdevloed merged 22 commits into
mainfrom
feat/unified-sidebar

Conversation

@bdevloed

Copy link
Copy Markdown
Contributor

The map UI pass of 2026-10-17/18 and its review round, reviewed on beta.openwinemap.com.

  • One sidebar: the Simple / Advanced toggle is gone (its chip filter had never filtered white or rosé); the style facet is the taxonomy tree with chevrons; Classification collapsed; Appellation type withheld (dormant) until AVAs arrive.
  • Search first, results inline instead of a popup; × and Esc clear; the sidebar folds to a rail on desktop.
  • Map chrome: fitted start view, zoom and scale controls, PDO / PGI / Spirits legend pills with ⓘ, hover highlight, the ⚙ menu (languages, light / system / dark as icon-only buttons).
  • Card: boundary notes folded below the facts, related appellations, compact stacks.
  • Phones: bar, drawer with scrim, 45 % sheet, safe areas; tablet overlay; light sidebar palette by tokens.
  • Countries and regions alphabetical; sub-denominations nest under their parent; names wrap.
  • Stage 04 --shell-only rebuilds the pages from the last full build's snapshot.
  • deploy.py: IndexNow fingerprints the page's content, never the shell; --indexnow-baseline, --no-indexnow.
  • Cleanup: dead grape-facet tokens removed at both ends (74 KB per bundle), obsolete msgids pruned, the gate test carries the PDO gate.

Verified headless (52 checks, axe, SSR fallback under Googlebot conditions at desktop and phone, locally and over the CDN); 1,268 tests pass.

🤖 Generated with Claude Code

bdevloed and others added 22 commits October 17, 2026 14:53
Since the zoom-LOD change the toggle only swapped the style facet (seven
bucket chips vs the taxonomy tree), hid the Classification and Appellation
type trees and gated spirits; 6.5 % of visitors ever touched it (Plausible,
90 days). One sidebar now:

- The style facet is the taxonomy tree alone: depth-0 rows in the chip order
  (FACET_BUCKET_ORDER — white, rosé, red, sparkling, sweet / dessert,
  oxidative, other; BUCKETS keeps the membership order the MCP context keys
  on), sub-styles folded behind a chevron. buildTreeFacet renders each row as
  a checkbox label with a sibling disclosure button (a button nested in the
  label would share its accessible name), the subtree as a hidden sibling
  wrapper; revealCheckedTreeRows opens the branches above a ticked row, so an
  omnisearch pick of "fino" is never hidden. The appellation tree's → button
  leaves the row's label for the same reason (.aoc-row).
- This also fixes a bug the chips carried: their expansion table
  (bucket_descendants) drops the bucket slug itself, so the "white" and
  "rosé" chips filtered nothing and "red" matched only clairet / primeur.
  Every style pick now expands through STYLE_DESCENDANTS (self included)
  and matches the records that carry the bucket slug.
- Classification and Appellation type are collapsed sections, always
  present. "Show PGIs" and "Include spirits" sit together in a scope row
  right under the count they explain; spirits are gated by that checkbox
  alone (default off, persisted). An omnisearch pick, WebMCP
  show_appellation or a spirit landing just turns it on.
- Gone: viewMode / applyMode / the toggle markup, CSS and labels, the
  pre-paint mode class, data-modes, the View Mode Switched event (retired
  in docs/analytics.md — archive the goal), the view_mode prop on the
  Feedback events (lod + zoom say what was on screen), localStorage.view_mode
  (removed once on load), and the dead #q search handler, .facet-search and
  #actions remnants; the close-panel focus fallback targets #omni.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A :root block declares the palette the map chrome shares — one accent
(--c-accent; the three former burgundies #934050 / #7A1F2B / #7a1f3a are
one), --c-pgi / --c-spirit / --c-cream, a neutral lightness scale (--n-0 …
--n-900, truthful on the dark sidebar and the light panel alike), the dark
panel pair and the amber note colours — and every recurrent literal in the
template's style block reads it through var(). Pixel-identical except for
the burgundy merge (feedback chips, the no-JS nav link and the skip link
take the accent). The browse page keeps its own CSS; the theme-color meta
is unchanged. tests/test_map_chrome.py pins the tokens and keeps --c-accent
/ --c-pgi in step with _build_source_block's paint.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- MapLibre NavigationControl (no compass) above the attribution and a metric
  ScaleControl bottom-left; the zoom buttons' accessible names come from the
  page's catalog (ctrl_zoom_in / ctrl_zoom_out) through the Map `locale`
  option, and are hidden on phones where pinch is the affordance. Dark-mode
  rules for the controls, the attribution and the scale.
- The legend rides the map (legendControl, bottom-left above the scale): a
  swatch and the scheme word for the PDO and PGI fills and, while spirits are
  shown, for the spirits colour — text beside every swatch, the long forms as
  tooltips. The sidebar legend block is gone with its grape-pill colour text
  (the panel's three grape headings already say it).
- Non-wine GIs (eaux-de-vie, ciders, spirit IGs) now paint in the spirits
  colour (#a07530, is_wine '0' tested before the PGI kind), so the legend's
  third swatch tells the truth.
- Hover, on pointer devices only: one hit test (hitSlugsAt — the click's
  4-px box over both fill layers, deduped, inheritors dropped, sorted by
  localityRank) serves hover and click; the polygon under the cursor takes a
  feature-state `hover` (brighter fill, +0.8 px outline, under the selection
  override on both sources) and a pointer-tracking label names it with "+N"
  for the rest of the stack a click would open. aria-hidden, no events.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mpact stacks

- The amber "how this polygon was drawn" lines and the zoom-reactive
  footprint line move below the terroir facts, folded under one summary
  ("About this boundary"; "· approximate area" when the shape stands in for
  the delimitation — a commune or administrative union, an inherited
  polygon, a whole-municipality zone). The stub line stays at the top. Both
  renderers (renderAocCard / content_block._boundary_notes) share the
  order and the approximation list; refreshLodLines keeps patching the
  footprint line inside the fold.
- Related appellations in the live card: a parent's sub-denominations
  (stage 04 writes `children` into the parent's panel payload — parents
  only, no startup field, no other payload changes — the names come from
  the startup record; 12 rows then "and N more") under the regulator's own
  heading (SUBDENOM_HEADINGS, now shared with app.js), and "All appellations
  in <region> · <country>" links that apply the tree's region / country
  filter and fit the map. The SSR card renders the country link as a real
  href to the browse index's country section (new `id="c-<cc>"` anchors;
  RenderCtx.browse_path).
- A stack renders the focus card in full and the rest as rows (name ·
  classification · region, a button each: click brings it to the front,
  hover lights its polygon); the "click again to cycle" hint is visible
  text. Only the focus record's detail is fetched (a 200-slug ?aocs= set
  no longer fetches 200 payloads). The subordinate-card markup, its ↑
  button and CSS are gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…verlay

Phones (≤768px): a slim bar — brand, search, menu — replaces the floating ☰
so the first screen is no longer a bare map; the drawer has a scrim (tap to
close), the bar buttons carry aria-controls / aria-expanded, opening moves
focus into the drawer (the search box from the search button), closing
returns it to the opener, Esc closes the drawer before the sheet, and the
drawer closes when a record is opened from the tree, the search or a
related-appellations link (setDrawer owns all of it; Drawer Opened event).
A record opens as a 45 % sheet (name, meta, styles in view, the polygon
still on screen) with a handle that expands it to 92 %, or expands as soon
as the reader scrolls it (Sheet Expanded event); the camera fit pads the
sheet and the bar (fitPadding), the legend and scale rise above the peeked
sheet; 100dvh and env(safe-area-inset-*) with viewport-fit=cover.

Tablets (769–1024px): the open panel overlays the map's right edge instead
of pushing it (300 + 440 px of chrome had left an iPad portrait an 80 px
map); the fit pads the overlay.

Boot: the panel and the drawer animate only after the first frames (html
.booted), so an entity landing paints its panel open instead of sliding it
in — the slide was most of the mobile CLS.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…at hold its matches

- The chrome's small text reads four tokens (--fs-2xs 10px, --fs-xs 11px,
  --fs-sm 12.5px, --fs-md 13px) instead of nine literal sizes between 9.5
  and 13.5px; shifts of at most half a pixel. Headings and the iOS 16px
  search box stay literal.
- A style / grape / classification / type filter used to leave its matches
  inside collapsed tree groups. Up to 25 matches now open the country and
  region groups that hold them (tagged, closed again when the filter clears
  — the search's auto-open pattern with its own flag); above that the
  status line offers "show in the list", which opens them and scrolls the
  tree into view.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…; analytics rows

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d VIVC brackets; drawer under the bar

axe-core (serious): #active-filters carried aria-label without a role —
role=group; the tree counts (#666 on the dark sidebar, 3.9:1), the scheme
word in the meta line (opacity .78) and the VIVC bracket in a grape pill
(opacity .65) were under 4.5:1 — explicit colours now. The phone drawer
opens under the bar instead of behind it, so the brand is never covered and
the menu button stays the visible way to close it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ast)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ones

- "Show PGIs" / "Include spirits" redraw in place (applyFilter() without
  fit): toggling only widens or narrows what is drawn where the reader
  already looks, it no longer refits the camera (feedback 2026-10-18).
- A tree row without children ends in a spacer of the chevron's width
  (28 px, 44 px on phones) and every row is the chevron's height, so the
  counts sit in one column at every breakpoint — the 27 px padding matched
  only the desktop chevron, and rows with and without a chevron differed
  in height ("wine styles looks messy").
- On phones no facet scrolls inside a 240 px box of its own: the drawer is
  the one scroll surface (the style facet hid its last two rows behind a
  nested scroll).
- Tests pin the handlers, the spacer markup and the CSS; CLAUDE.md updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On phones the drawer opens under the bar, so its own brand block (logo,
name, tagline) repeated "Open Wine Map" 50 px below the bar's. The block
is now visually hidden there — kept in the DOM, since it is the page's
<h1> on the home and fold pages (assistive tech, mobile-first crawl) —
and the drawer opens on the language row; the bar carries the brand.
Desktop is unchanged (no bar). Feedback 2026-10-18.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, ⓘ hints on the scope toggles

Three phone-review points (2026-10-18):
- The three facet trees share the appellation list's row rhythm (24 px
  rows + padding ≈ 28 px) on every breakpoint; the 44 px phone rows read
  as padding next to the country rows. The chevron keeps a 36 × 24 px
  target on phones, the WCAG AA minimum.
- Only the appellation tree starts open: Wine style collapses like
  Classification and Appellation type. revealCheckedTreeRows opens a
  section whose tree holds a tick, so a search pick stays visible.
- Each scope toggle carries an ⓘ (scope-hint, data-key pgi / spirit-gi)
  that opens the scheme definition the card's pills already carry —
  TERMS_INFO through the same tooltip — by hover, focus or a tap. Touch
  drives it through the click alone (a tap toggles): a tap is a burst of
  synthetic mouse events whose trailing mouseout would close what the tap
  opened. New msgids scope_hint_pgi / scope_hint_spirits (en / es / nl).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ld's snapshot

A template, JS, CSS or catalog change re-renders every page, but nothing
the page emitter consumes has changed. The emitter reads the two feature
lists (properties only — the geometry is tiled, never rendered into a
page) and 23 runtime caches main() fills while loading, augmenting and
resolving the records. Every full build now pickles exactly those at the
emit boundary (scripts/_lib/shell_snapshot.py →
raw/cache/stage04-shell-snapshot.pkl, 6 MB) and `--shell-only` restores
them in place and calls the emitter: 46 s against 706 s for the full
build, byte-identical output (golden comparator; the first run exposed
the two geom_chain provenance dicts the state map had missed — the
Galician records' IET attribution — now included). The tile fingerprints
are recomputed from disk; the flag warns, file by file, when a non-shell
source or an extracted-corpus _index.json is newer than the snapshot,
and exits 2 without a snapshot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…menu, folding sidebar, search first, nested sub-denominations

The UX/UI feedback session (Miro board + eleven notes), applied:

- The start view fits the corpus (every record's bbox except the Atlantic
  islands west of 12° W); a URL hash still wins. No card is reopened from
  localStorage on the next visit (Penedès had been left open for the next
  visitor); the stored key is removed on load.
- The PDO / PGI / spirits scope toggles are legend pills on the map, one per
  scheme, each with its ⓘ scheme definition; PDOs can be hidden too (a third
  gate in the filter expression, the client matcher, the landing gate and
  openAppellation). A toggle never moves the camera.
- Language and the three-state theme switch (now with its words) move into a
  ⚙ control top-right of the map; the block stays server-rendered in the
  sidebar footer for a no-JS visitor.
- The desktop sidebar folds to a 48 px rail (pin, magnifier, ›), restored
  before first paint; the rail's magnifier unfolds with the search focused.
- Search first, taller, with a magnifier and a × clear button; Esc backs out
  one step at a time (the list, the text, then the drawer / panel).
- Countries and regions sort alphabetically in the page's language.
- Sub-denominations nest under their parent behind a chevron, the parent's
  name cut from theirs ("premier cru Les Vercots"); appellation names wrap
  instead of truncating; the tree filter opens a fold whose child matches.
- The sidebar, the phone bar and the ⚙ menu take a role-based palette
  (--side-*), light by default and dark under html.theme-dark.

Headless: 44 checks (start zoom, no restore, order, pills, hints, menu,
theme, fold, search, nesting, wrapping, hash override, entity landing,
phone) and axe at zero serious violations on seven states; the no-JS
fallback paths unchanged. Analytics: Kind Toggled gains kind=pdo; new
Settings Opened, Sidebar Toggled, Search Cleared.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…footer sits at the bottom

The footer (the auto-generated-map disclaimer and the two links) floated
mid-sidebar under the appellation list's 340 px scroll box. The sidebar is
a flex column now: on desktop the open appellation <details> takes every
pixel left and is its own scroll box with a sticky summary (its content
sits in the element's shadow slot, where a flex rule on the inner .facet
cannot reach), so the footer is pinned at the bottom whatever the list's
length; phones keep the drawer as the one scroll surface.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…hone bar

Phone review, 2026-10-18:

- The search results are no longer a popup. While the box holds text they
  replace the filters below it (#sidebar.searching hides the facets, the
  count and the chips) and scroll with the sidebar, on desktop and phone —
  the dropdown that sat half over the tree read as a layout error, and its
  content overlapped the tree it covered. 25 appellations and 6 of each
  other group; no seeds for an empty box; Esc or × clears the text and
  brings the filters back.
- On a phone the ⚙ and its menu are homed into the bar beside the ☰
  (homeSettings, re-homed when the viewport crosses 768 px); the map's
  control is hidden there. The bar's search button went: it opened the
  drawer the ☰ opens, the search being the drawer's first row anyway.
- The bar's button rule is scoped to its direct children so the menu's
  theme buttons keep their own shape.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lation-type section

Phone review, 2026-10-18:

- The active-filter chip overhung the content edge and sat on the header
  below: the sidebar is a flex column and the tray carried min-height:0,
  so an overflowing drawer squeezed it to nothing. No child of the column
  shrinks now (the open appellation tree flexes by its own rule); the tray
  takes the 16 px gutter, breathes above the next header and sizes its
  chips and × targets for phones.
- The theme switch's ☀ ◐ ☾ glyphs rendered as emoji or text by platform
  and each cell sized to its label; three equal cells with small inline
  SVG icons instead.
- The Appellation type facet is withheld from the sidebar for now (to come
  back when AVAs etc. are introduced): only its <details> is removed, the
  builder, data tokens and chip handling stay dormant behind null-guards.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The words made the three cells cramped ("System" touched its edges at
12.5 px). The theme switch is one row now — the heading left, three
icon-only buttons right: sun / monitor / moon (the convention; the
half-circle stood for nothing), the word as each button's aria-label
and title, the pressed one filled with the accent. 36×30 px on desktop,
44×36 on phones. Tests and docs follow; the session memory too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…'s content

Dead code the 2026-09-23 search rail had left behind, removed at both ends:
the three grape-facet lists (FACET_PRINCIPAL / ACCESSORY / GRAPES_ALL),
SLUG_TO_CANONICAL and GRAPE_SYNONYMS with grapeSynonymsHtml, frMarker,
_bassin_match_expr — 74 KB off each app bundle, eslint at zero warnings;
five comments that still described the dropdown, the scope row and the
bar's search button; the 47 obsolete msgids and the FR fuzzy entry.

deploy.py: the IndexNow fingerprint covers a page's content — title, the
description / robots / Open Graph metas, canonical and hreflang links,
JSON-LD, the server-rendered card, the entity nav — never the sidebar,
the panel skeleton, the viewport meta or the scripts, so a UI release
pings nothing. --indexnow-baseline SITE_DIR re-records the set from a
snapshot after a scope change; --no-indexnow uploads without submitting.

tests: the omnisearch gate harness carries the PDO gate of 2026-10-18 (it
failed on showPdo) and a case per gate; fingerprint scope and baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bdevloed
bdevloed merged commit 5caaeb5 into main Oct 19, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant