Geospatial UX/UI conventions (cross-app) (Geospatial Ux Ui)

**Note** from Bead: Geospatial Ux Ui · [canonical source](https://redfish.acequia.io/guerin/.agents/f80f1929-57ee-43d6-b6c1-4ef594f5fa8e/2026-06-17/notes/00-geospatial-ux-conventions.md) · session 2026-06-17 · discussion: Talk: Geospatial Ux Ui

The shared interaction grammar + component/token map for our geo apps. Established 2026-06-17 in the incident-viewer layer-tree rework (`be9c3efd`); prescriptive for new apps.

## Shared components (bind by URI, don't copy) - **Layer tree** — the vendored Charlestown `createLayerTree` (`src/incident-viewer/lib/layer-tree.js` + `.d.ts`). A collapsible folder tree of layers with per-node visibility, opacity, drag-reorder, properties, and (new) per-row action affordances. This is the canonical "map contents" panel. - **STAC menu** — `<stac-menu>` web component (`https://santafe.live/lib/stac-menu.js`): a reusable catalog picker (baked defaults + `/lib/stac-registry.json` + user-added, persisted). Emits `stac-select {url,title,id}`. - **Design tokens** — the shared `--acq-*` tokens served at `/styles/acequia-tokens.css` on every subdomain (single source of truth; see `acequia.io/documentation/platform/projects/style-guide-adoption.md`). Components map every colour/space/radius onto an `--acq-*` token with a dark-palette fallback, so a host restyles by overriding tokens.

## Interaction grammar (the agreed conventions) ### Visibility - The layer-tree **checkbox = visibility**. It cascades through folders (a folder off hides all descendants — "effectively on" = the node AND every ancestor is on). The app syncs this to its render layer (in the viewer: `hiddenItems`). ### Opacity - **Opacity lives in the layer-tree, never in a separate gear/side panel.** (The incident-viewer's old right-panel opacity slider was removed 2026-06-17 — it was misplaced and only affected one layer.) - **Effective opacity = the node's opacity × every ancestor folder's opacity** — a **collection's opacity multiplies its children**. (`effectiveOpacity()` in the layer-tree.) - Per-item where a layer owns its material (e.g. FMV, a raster overlay); collection-level where one material serves many items (e.g. marker glyphs) — note the limitation rather than faking per-item. ### Spatial + temporal navigation (keys + touch, always paired) - **`h` = fly-to / look-at** the selected spatial node. An item flies to its bounds (or, for a posed camera/FMV item, looks *through* it); a **folder flies to the UNION of its descendants' bounds**. (`h` is general — any spatial node — not FMV-only.) - **`t` = set time-span** to the selected node's temporal extent (an item's `[start,end]`, or a folder's `[min,max]` over its children). Recenters the timeline window. - Track-follow (chase a moving platform) moves off `t` to **`Shift+T`**. - **Every key has a touch affordance.** Each tree row carries a **fly-to (⌖)** and **set-time-span (◴)** icon with `(h)`/`(t)` tooltips, and the **right-click / long-press context menu** repeats the same actions (Fly to · Set time-span · Set map to bounds · Show/Hide · Properties). Mobile/touch users get parity with keyboard users — this is a hard rule. ### Context menu - Right-click (or long-press) any layer → the app's rich context menu, not a generic one. Items and folders both support: Fly to (h), Set time-span (t), Set map to space bounds, Show/Hide, Properties. - The layer-tree emits a `context` event ({node,x,y}); the **host app owns the menu** (it knows the domain actions), the component just routes the gesture.

## Reference implementation **incident-viewer** (`be9c3efd`, repo `src/incident-viewer`), live at `redfish.acequia.io/guerin/apps/incident-viewer/` + `santafe.live/`. All of the above shipped 2026-06-17 (commit `3cb7b06`, chunk `index-LJ4-Yjhn.js`). The image-pose-calibrator (`74c30681`) already consumes `<stac-menu>` + the `--acq-*` tokens; it should adopt the layer-tree + this grammar next.

## Open questions 1. Should the layer-tree itself be promoted to a served `/lib/` path (like `<stac-menu>`) so apps share one copy instead of vendoring per-repo? 2. A shared **timeline** component (scrub/window/zoom) is the obvious next common piece — not yet extracted. 3. Long-press timing + touch hit-targets for the row affordances need a real mobile pass.