Skip to content

[menu] Always highlight the first item on open#4816

Closed
chsmc-ant wants to merge 1 commit into
mui:masterfrom
chsmc-ant:menu-focus-item-on-open
Closed

[menu] Always highlight the first item on open#4816
chsmc-ant wants to merge 1 commit into
mui:masterfrom
chsmc-ant:menu-focus-item-on-open

Conversation

@chsmc-ant
Copy link
Copy Markdown

When a menu is opened programmatically (via controlled open or by calling onOpenChange(true) from a custom interaction that isn't one of Menu.Trigger's built-in open keys), useListNavigation leaves activeIndex as null. Its default focusItemOnOpen: 'auto' only highlights an item when Floating UI's own trigger handlers set the internal keyRef/focusItemOnOpenRef — which doesn't happen for external opens. Every item then renders with tabindex="-1", FloatingFocusManager parks focus on the popup container, and the user needs an extra press before arrow navigation works.

This PR passes focusItemOnOpen: true to useListNavigation in MenuRoot, so the first item is highlighted on open regardless of how the menu was opened. ArrowUp-on-trigger continues to highlight the last item because keyRef still takes precedence inside useListNavigation's initial-sync effect.

Behavior change

This also means pointer-driven opens (mouse/touch click on the trigger) now highlight the first item instead of leaving the popup container focused. Two existing tests have been updated to reflect that:

  • MenuRoot.test.tsx — removed one ArrowDown from the nested-menu outside-click test (item 1 is now focused after click).
  • Menubar.test.tsx — the "keyboard navigation after mouse click" test now asserts item 1 is focused immediately, without a leading ArrowDown.

Related: #4815 adds an imperative actionsRef.setActiveIndex escape hatch as a non-breaking alternative; this PR is the broader behavior fix and can land independently or instead of it.

@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 12, 2026

Bundle size

Bundle Parsed size Gzip size
@base-ui/react 🔺+19B(0.00%) 🔺+3B(0.00%)

Details of bundle changes

Performance

Total duration: 1,126.39 ms +34.24 ms(+3.1%) | Renders: 50 (+0) | Paint: 1,731.60 ms +67.81 ms(+4.1%)

Test Duration Renders
Checkbox mount (500 instances) 75.88 ms 🔺+14.06 ms(+22.7%) 1 (+0)

11 tests within noise — details


Check out the code infra dashboard for more information about this PR.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 12, 2026

Deploy Preview for base-ui ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit e1cad93
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/6a03b3b2af012b0008abfbf7
😎 Deploy Preview https://deploy-preview-4816--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@oliviertassinari oliviertassinari changed the title [Menu] Always highlight the first item on open [menu] Always highlight the first item on open May 12, 2026
@oliviertassinari oliviertassinari added the component: menu Changes related to the menu component. label May 12, 2026
@colmtuite
Copy link
Copy Markdown
Contributor

Hey @chsmc-ant 👋

This also means pointer-driven opens (mouse/touch click on the trigger) now highlight the first item instead of leaving the popup container focused.

This part won't work. The current behaviour is correct, at least by default.

  • Clicking the trigger does not focus the first menu item.
  • Pressing Space on the trigger does focus the first menu item.
  • Pressing Enter on the trigger does focus the first menu item.
  • Pressing Down on the trigger does focus the first menu item.
  • Pressing Up on the trigger does focus the last menu item.

It's the same across Base UI, React Aria, Ariakit, and Radix.

The reason is that keyboard activation is a commitment to operate the menu. Pointer activation is often just revealing the menu before the user has chosen where to go.

When the user opens by keyboard, focus should enter the composite widget because keyboard users need a focused item to continue. When the user opens by pointer, focus does not need to jump to an arbitrary item because the pointer itself is doing the navigation.

@chsmc-ant
Copy link
Copy Markdown
Author

Hey @colmtuite that makes sense and thank you for the response! I opened up this PR as an alternative approach to #4815 which I think is a better way to make the desired behavior for us possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: menu Changes related to the menu component.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants