Context
Operator (2026-09-16): keyboard navigation through nested menus in examples/button/base once worked — arrows plus Space/Enter walked the cascade — and regressed when main.addon.Navigator arrived (#5123). The operator also ruled the pre-Navigator model out as the target: it selected on navigate and had no focus state. Focus as the cursor, with Space/Enter as the explicit select, is the intended UX.
Observed on dev@d688710175, examples/button/base, in-app browser with CDP key events. Each step starts from the previous one:
| Step |
Result |
Click Hello World |
Menu shows, focus on Item 1 |
| ArrowDown ×2 |
Focus on Item 3 (a parent), nothing shown — correct under the focus model |
ArrowRight on Item 3 |
nothing |
Enter on Item 3 |
Submenu shows, focus moves to Item 4 |
| ArrowDown |
Focus on Item 5 |
ArrowLeft on Item 5 |
nothing: submenu stays, focus stays inside it |
The Problem
#5123 (2023-12-22) moved arrow navigation out of selection.ListModel#onNavKey, which selected the next item, into the Navigator, which moves focus. It left onKeyDown{Up,Down,Enter,Left,Right} as placeholders. selection.menu.ListModel still assumes selection is the cursor:
onKeyDownRight() calls view.activeSubMenu?.selectionModel.selectAt(0). A focused parent has no active submenu, so Right is a no-op. When one is showing, Right moves that submenu's selection, not focus.
onKeyDownLeft() re-selects parentMenu's item at parentIndex. It is already selected, so nothing changes: the submenu is never hidden and focus never moves.
- Space: the Navigator activates on Enter only (
case 'Enter' in its keydown handler), and no menu binds Space. This is a code reading, not yet run: the in-app browser's key instrument sends no key value for Space.
Enter works through a side effect: the Navigator clicks the item, selection shows the submenu, and component.Base#afterSetMounted focuses a floating component's first focusable child on mount.
The Fix — src/selection/menu/ListModel.mjs, src/menu/List.mjs
Inside the focus model (arrows move focus; Enter, Space and click select and activate):
- Right on a focused parent shows its submenu and focuses the submenu's first item, including when the submenu is already showing. On a leaf: nothing.
- Left in a submenu hides it and focuses its parent item. In the root: nothing.
- Escape in a submenu hides that level and focuses its parent item. In the root: today's dismissal.
- Space on a focused item does what Enter does. The binding is menu-local; the Navigator stays unchanged for every other list.
- Up/Down stay focus-only: arrowing onto a parent shows nothing.
The focused item comes from the view's focus state (list.Base#focusIndex, which onListNavigate() already keeps current), never from selection.
Acceptance Criteria
Out of Scope
- Reintroducing selection on navigate (operator ruling above).
- Pointer hover — #18759.
- Changing the Navigator's key handling for other lists.
Follow-up — focus-rest preview (operator direction, 2026-09-16)
Keyboard focus resting on a parent would show its submenu the way a pointer rest does (#18759), and Up/Down off it would hide it again; Right then only moves focus into what is already showing. It needs #18759's non-focusing preview show — without it, the mount-focus above pulls the cursor into every previewed submenu — and it changes this ticket's Left to "focus the parent, keep its submenu showing". File when this ticket and #18759 have both merged.
Related
Blocked by #18758 · #5123 · #4606 · #4605
Live latest-open sweep: latest 20 open issues at 2026-09-16T08:07:16Z; no equivalent found
A2A claim sweep: latest 30 inbox messages, all read-states, at 2026-09-16T08:07Z; no [lane-claim] / [lane-intent] on menus
MC sweep: query_raw_memories on the symptom ("keyboard navigation nested menu submenu arrow right left does nothing; menu keynav regression after Navigator addon"), 6 results; a #14619-era note records Space as an uncovered edge of Enter-primary activation — consistent, no decision against. Git history: 00a67f1020 (#5123) removed onNavKey
Own-assignment sweep: 5 open, none on src/menu or src/selection
Origin Session ID: fda7faab-19c5-43a0-b192-003966068172
Retrieval Hint: "menu.List keynav Right Left Escape Space focus model Navigator #5123 selection.menu.ListModel onKeyDownRight"
Context
Operator (2026-09-16): keyboard navigation through nested menus in
examples/button/baseonce worked — arrows plus Space/Enter walked the cascade — and regressed whenmain.addon.Navigatorarrived (#5123). The operator also ruled the pre-Navigator model out as the target: it selected on navigate and had no focus state. Focus as the cursor, with Space/Enter as the explicit select, is the intended UX.Observed on
dev@d688710175,examples/button/base, in-app browser with CDP key events. Each step starts from the previous one:Hello WorldItem 1Item 3(a parent), nothing shown — correct under the focus modelItem 3Item 3Item 4Item 5Item 5The Problem
#5123 (2023-12-22) moved arrow navigation out of
selection.ListModel#onNavKey, which selected the next item, into the Navigator, which moves focus. It leftonKeyDown{Up,Down,Enter,Left,Right}as placeholders.selection.menu.ListModelstill assumes selection is the cursor:onKeyDownRight()callsview.activeSubMenu?.selectionModel.selectAt(0). A focused parent has no active submenu, so Right is a no-op. When one is showing, Right moves that submenu's selection, not focus.onKeyDownLeft()re-selectsparentMenu's item atparentIndex. It is already selected, so nothing changes: the submenu is never hidden and focus never moves.case 'Enter'in its keydown handler), and no menu binds Space. This is a code reading, not yet run: the in-app browser's key instrument sends no key value for Space.Enter works through a side effect: the Navigator clicks the item, selection shows the submenu, and
component.Base#afterSetMountedfocuses a floating component's first focusable child on mount.The Fix —
src/selection/menu/ListModel.mjs,src/menu/List.mjsInside the focus model (arrows move focus; Enter, Space and click select and activate):
The focused item comes from the view's focus state (
list.Base#focusIndex, whichonListNavigate()already keeps current), never from selection.Acceptance Criteria
Item 3→Item 6→Item 7, then back up toItem 3— with focus on the expected item at every step.Out of Scope
Follow-up — focus-rest preview (operator direction, 2026-09-16)
Keyboard focus resting on a parent would show its submenu the way a pointer rest does (#18759), and Up/Down off it would hide it again; Right then only moves focus into what is already showing. It needs #18759's non-focusing preview show — without it, the mount-focus above pulls the cursor into every previewed submenu — and it changes this ticket's Left to "focus the parent, keep its submenu showing". File when this ticket and #18759 have both merged.
Related
Blocked by #18758 · #5123 · #4606 · #4605
Live latest-open sweep: latest 20 open issues at 2026-09-16T08:07:16Z; no equivalent found A2A claim sweep: latest 30 inbox messages, all read-states, at 2026-09-16T08:07Z; no
[lane-claim]/[lane-intent]on menus MC sweep:query_raw_memorieson the symptom ("keyboard navigation nested menu submenu arrow right left does nothing; menu keynav regression after Navigator addon"), 6 results; a #14619-era note records Space as an uncovered edge of Enter-primary activation — consistent, no decision against. Git history:00a67f1020(#5123) removedonNavKeyOwn-assignment sweep: 5 open, none onsrc/menuorsrc/selectionOrigin Session ID: fda7faab-19c5-43a0-b192-003966068172 Retrieval Hint: "menu.List keynav Right Left Escape Space focus model Navigator #5123 selection.menu.ListModel onKeyDownRight"