microsoft / microsoft/fluentui
[Bug]: Escape in a Menu nested in a groupper/modalizer escapes the groupper
@layershifter is already working on this.
Since Jun 16, 2026.
- Dominant language
- TypeScript
- Stars
- 20.3k
- Forks
- 2.9k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 46
Description
Component
Menu (@fluentui/react-menu)
Package version
@fluentui/react-menu@9.25.0 — regression vs 9.21.2.
React version
18.3.1 (also reproduces on React 17 / 19).
Environment
Any browser — this is a Tabster focus-management timing issue, not browser-specific.
Current Behavior
When a Menu is nested inside a Tabster groupper + modalizer (e.g. a focusMode Card, or a list item whose hover/actions popover shares a modalizer id with the item), pressing Escape to close the menu moves focus to the groupper root instead of restoring focus to the menu trigger.
Expected Behavior
Closing the menu with Escape restores focus to the menu trigger (e.g. the "More options" button), as it did in @fluentui/react-menu@9.21.2.
Reproduction
https://github.com/layershifter/fluent-v9-menu-groupper-esc-repro
npm install && npm run dev for the interactive demo (with surfaceMotion toggles), or npm test for the deterministic Playwright assertions.
Steps to reproduce
- Focus the "message" group (a groupper + modalizer) — this activates the modal scope.
- Move focus into the actions popover and onto the "More options"
Menutrigger. - Press ↓ to open the menu (focus moves to the first item).
- Press Esc.
- Actual: focus lands on the message group. Expected: focus returns to the "More options" trigger.
Root cause
react-menu@9.25.0 wraps the menu popover in a surfaceMotion presence component (presenceMotionSlot, unmountOnExit), which defers the popover unmount by a tick.
Pre-9.25.0 the popover unmounted synchronously on close; removing the focused menu item dropped focus to <body>, which fired Tabster's restorer source → a higher-priority Restorer async-focus intent that pre-empted the groupper's pending EscapeGroupper intent. With the deferred unmount, react-menu moves focus menu-item → trigger directly (never through <body>), so the restorer never arms and the groupper-escape fires and steals focus to the group.
Tabster itself is unchanged between 9.21.2 and 9.25.0 — only the menu's unmount/restore timing changed. surfaceMotion={null} on the <Menu> restores the synchronous unmount and fixes it; surfaceMotion={false} does not (only null is special-cased by presenceMotionSlot).
Note: this is the same class of bug already fixed for Combobox/Dropdown in #36275 (event.stopPropagation() can't stop Tabster because it listens in the capture phase).
Are you reporting an Accessibility issue?
Yes — focus is not restored to the trigger on dismiss (WCAG 2.4.3 Focus Order).
Related
- Fix: #36313 — adds
focusable.ignoreKeydown: { Escape }on theMenuPopover(mirrors #36275). - Same class of bug, fixed for react-combobox: #36275.
- Prior art on Menu's restorer behaviour: #32840.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.