Context
Operator, live session 2026-08-17 ~19:00Z: "i click on memories inside the right side nav => overlay appears. if i click anywhere inside the overlay content except for a button, it closes / hides." For a reading surface (session-summary prose), nearly every honest interaction — click to read, click to select text, click whitespace — dismisses the overlay.
The Problem (mechanism read at the source, line-exact)
src/dashboard/DockRevealOverlay.mjs:356-368 — the dismiss contract's own words: "Clicking anywhere outside a focused overlay moves focus out, so outside-click dismissal of a focused reveal is embodied here" (onFocusLeave → revealFocusLeave → the rail's reveal machine dismisses).
The embodiment assumes click-inside ⇒ focus-stays-inside. Browsers break that assumption for non-focusable targets: clicking prose, whitespace, or any non-interactive element inside the overlay sets focus NOWHERE in the subtree (it falls to body) → manager.Focus reports a genuine subtree leave → dismissal fires. Buttons keep the overlay open precisely because they are focusable. A pane that is mostly text is therefore mostly dismiss-surface — the exact operator experience.
The machine otherwise does this well: hover wobble rides a dismiss-pending grace window, Escape works, the Pin escape persists. Only the click-born focused reveal's inside/outside discrimination is wrong.
The Fix (directions — implementer picks, states which)
- Contain the click: the overlay content root becomes programmatically focusable (
tabIndex: -1) and a pointerdown inside the subtree (re-)focuses it before the browser defaults — inside clicks then genuinely keep focus inside, and the existing focus-leave contract becomes TRUE instead of replaced. Text selection must survive (focus the root, do not preventDefault the selection).
- Or discriminate by pointer: dismissal keys on
pointerdown target containment (outside the subtree) rather than focus-leave alone; focus-leave stays for keyboard tab-out.
- Either way: clicking prose/whitespace inside NEVER dismisses; outside-click, Escape, re-click-tab, and pin behavior unchanged.
Acceptance Criteria
Out of Scope
Pane content design (#17268) · rail/tab interaction vocabulary (#17269) · the shell containment sibling (#17313).
Related
#14617 (the auto-hide rail arc this surface belongs to) · #17313 (shell-geometry sibling from the same session) · #17268 (Memories pane as the operator-hit fixture) · Epic #14560 (consumer; this ticket is engine-side)
Live latest-open sweep: REST created-descending checked 2026-08-17T19:04Z, no equivalent; A2A window clean.
Origin Session ID: 7ee47ccf-d1c7-469d-a75e-15cebf3b5ea5
Retrieval Hint: query_raw_memories("reveal overlay dismiss inside click non-focusable focus leave body")
Context
Operator, live session 2026-08-17 ~19:00Z: "i click on memories inside the right side nav => overlay appears. if i click anywhere inside the overlay content except for a button, it closes / hides." For a reading surface (session-summary prose), nearly every honest interaction — click to read, click to select text, click whitespace — dismisses the overlay.
The Problem (mechanism read at the source, line-exact)
src/dashboard/DockRevealOverlay.mjs:356-368— the dismiss contract's own words: "Clicking anywhere outside a focused overlay moves focus out, so outside-click dismissal of a focused reveal is embodied here" (onFocusLeave→revealFocusLeave→ the rail's reveal machine dismisses).The embodiment assumes click-inside ⇒ focus-stays-inside. Browsers break that assumption for non-focusable targets: clicking prose, whitespace, or any non-interactive element inside the overlay sets focus NOWHERE in the subtree (it falls to
body) →manager.Focusreports a genuine subtree leave → dismissal fires. Buttons keep the overlay open precisely because they are focusable. A pane that is mostly text is therefore mostly dismiss-surface — the exact operator experience.The machine otherwise does this well: hover wobble rides a
dismiss-pendinggrace window, Escape works, the Pin escape persists. Only the click-born focused reveal's inside/outside discrimination is wrong.The Fix (directions — implementer picks, states which)
tabIndex: -1) and apointerdowninside the subtree (re-)focuses it before the browser defaults — inside clicks then genuinely keep focus inside, and the existing focus-leave contract becomes TRUE instead of replaced. Text selection must survive (focus the root, do not preventDefault the selection).pointerdowntarget containment (outside the subtree) rather than focus-leave alone; focus-leave stays for keyboard tab-out.Acceptance Criteria
onFocusLeavedoc sentence updated to match the mechanism that ships.Out of Scope
Pane content design (#17268) · rail/tab interaction vocabulary (#17269) · the shell containment sibling (#17313).
Related
#14617 (the auto-hide rail arc this surface belongs to) · #17313 (shell-geometry sibling from the same session) · #17268 (Memories pane as the operator-hit fixture) · Epic #14560 (consumer; this ticket is engine-side)
Live latest-open sweep: REST created-descending checked 2026-08-17T19:04Z, no equivalent; A2A window clean.
Origin Session ID: 7ee47ccf-d1c7-469d-a75e-15cebf3b5ea5
Retrieval Hint:
query_raw_memories("reveal overlay dismiss inside click non-focusable focus leave body")