soul.demarkus.iosoul.demarkus.io/demarkus-library/adr/0006-reading-room-interaction-overlays.md accepted reader metaADR 0006 — Reading Room interaction model: dock, palette, and on-demand overlays
Status: accepted, accruing. This is the umbrella ADR for the reading-room
interaction model that grew on top of ADR 0005
(the spatial trail). Its sections are referenced throughout the
internal/adapter/inbound/web code as "ADR 0006 §N". The doc was created late
(the sections shipped before it was written down), so §0 through §5 are recorded
here as a descriptive index, grounded in the shipped code and templates; their
full rationale lives in the originating PRs and journals. The §6 universe
overlay below is recorded in full, as the section added with that work
(PR #47, merged to main 2026-06-22).
Section index (§0–§6)
These are the affordances that sit on the spatial trail (ADR 0005). Each is server-rendered, htmx-driven, and degrades without JS (ADR 0003).
- §0 — Reading-room shell / nav. The page chrome: title, nav, the
/→/t/uentry that makes the universe floor the landing. - §2 — The dock. The bottom orientation strip: a
<details>trail summary with walk/jump connectors and "from here →" one-hop neighbor chips. Zero-JS minimize. - §3 — The command palette + active search. ⌘K palette (htmx results fragment) and the htmx active-search overlay. Replaces ADR 0005's removed global search box; lookup still lives in the catalog/tag panes.
- §4 — The graph overlay. On-demand pull-up of the focused document's
reference neighborhood (
g/ margin "graph" link). Server-rendered SVG, embedded hidden, summoned by islands.js; node clicks are trail jumps, so navigating dismisses it. Replaces the in-trail graph pane. - §5 — The world map + rich index. One zoom level into a single world: the
catalog as a reference-connectivity SVG (
/w/:world/u), lazily htmx-loaded into themoverlay; plus the rich directory index (titles over a bare ls). The universe lists worlds as door cards by default; the map is the deliberate secondary view.
§6 — Universe overlay (added 2026-06-22, PR #47, merged to main)
Context
The universe floor (ADR 0005 §4: "the universe view/floor is pane zero") was the
one map still trapped as a trail pane. Its floorSVG laid worlds in a single
horizontal row whose width was set by whichever band (systems or portals) was
wider, so a lone world slid to the corner, and the row grew without bound as
worlds multiplied. A trail pane also gives the universe no room: it is squeezed
to pane width while the rest of the canvas competes for space. The graph (§4) and
world map (§5) had already solved exactly this with full-viewport on-demand
overlays; the floor had not been given the same treatment.
Decision
Give the universe floor the on-demand overlay treatment, in parity with §4 and §5: a full-viewport lens summoned on demand, plus a layout that scales to a large universe instead of one ever-widening row.
-
The floor's "view as map" link summons a full-viewport overlay, rather than rendering the map inline in the pane.
GET /u?overlay=1returns the barefloorSVGfragment, trail-aware fromHX-Current-URL; a direct/uhit redirects to/t/u(the floor has no standalone permalink — its home is pane zero). AFloorHasflag on the canvas VM renders the#universe-overlayshell whenever the floor pane is present; islands.js lazily htmx-loads the fragment into#universe-canvason summon. Reuses the §4/§5 overlay chrome (.graph-backdrop/.graph-panel/.graph-canvas). -
Progressive enhancement. The trigger carries
hx-boost="false"so htmx does not boost-navigate the click; islands.js intercepts it to open the overlay. With JS off, the same href is a real?view=mapthat renders the map inline on the floor pane. The topology stays reachable either way (ADR 0003). -
Landing-only reach. The trigger lives only on the floor pane (the landing), not as a global nav affordance. To pull up the universe you return to the landing (pane zero / one dock click away). Decided with Fritz over adding a persistent nav element.
-
The floor renders worlds-as-cards by default; the overlay is the map. No change to the ADR 0005 §5 cold-entry default — the overlay is the spatial topology that needs the real estate, not a second home for the cards.
-
floorSVGis laid out as centered grids, not single rows. Systems up top, portals in a band below, each band wrapping at roughly √N landscape- biased columns and centered within the canvas, so a lone world sits in the middle and the universe grows downward into the scrollable overlay. Width is bounded by column count; height grows with rows. -
One accessibility implementation across all three overlays. A shared
showOverlay/hideOverlayfocus helper (islands.js) moves focus into the panel on open and restores it on close — to the trigger link for a click, to the active element for a hotkey toggle. Each overlay panel carriesrole="dialog",aria-modal="true", andaria-labelledbypointing at its title. The map overlay gained these in the same pass (it lacked them); the universe one was aligned to the panel to match the graph overlay.
Envelope
Web-layer only. No core / port / service / domain change, no broker / worlds /
agent / deploy change, reader-only. The floor data (Floor / FloorCached) and
floorSVG already existed; this adds a fragment route, an overlay shell, the
islands.js glue, and the grid layout. Both transports (ADR 0005 §16) are
unaffected — the floor renders the home world in QUIC mode and the full universe
through the broker.
Consequences
- The universe map has room to breathe and the layout scales: many worlds wrap and center rather than sliding off one edge.
- The three pull-up overlays (graph §4, world map §5, universe §6) now share one focus + ARIA implementation; the map and graph overlays inherited the focus management and the map inherited dialog ARIA in this pass.
- Open follow-up — satellite density at very large N. Each system still draws its orbiting top-docs, so at tens of worlds the grid is correct and scrolls but each cell is busy. Capping satellites per world (or showing them only on zoom into a world) is the next increment. Same family as the roadmap's "graph-pane aggregation/cap for high-degree hubs".
Alternatives rejected
- A global nav affordance for the universe (always-present trigger). Rejected for landing-only reach; the floor is already pane zero and one click away.
- Overlay-only floor / map-first landing. Rejected; the worlds-as-cards cold-entry (ADR 0005 §5) stays the default, and the overlay is additive.
- Link-graph clustering (Leiden) to organize the layout. Investigated and shelved: measured on the real soul graph it mostly re-derived the directory tree (hub-and-spoke topology, no real community structure), so it did not earn a place in the floor. Recorded in the soul under the demarkus project journal.