Skip to content

Track D9Menu's position only while it is open - #24

Merged
PhilReinking merged 1 commit into
mainfrom
fix/bits-208-menu-closed-tracking
Oct 11, 2026
Merged

PhilReinking merged 1 commit into
mainfrom
fix/bits-208-menu-closed-tracking

Conversation

@PhilReinking

Copy link
Copy Markdown
Contributor

D9Menu now renders its floating wrapper only while the menu is open. Closed menus no longer keep floating-ui's scroll and resize listeners, a ResizeObserver and an IntersectionObserver alive. Input's editor sidebar has one menu per block, so this grows with the form.

Fixes BITS-208

  • MenuItems is static, so the items stay visible while the wrapper plays its leave transition.
  • useFloating positions with left/top (transform: false), so the wrapper's slide-in translate isn't overridden.
  • Props, slots, events and keyboard behaviour are unchanged. Input's four D9Menu call sites don't rely on the wrapper while closed, so Input needs no change.

Tested: check, build and tests pass; the new Menu spec fails on main. Headless Brave on Storybook: all five Menu stories, plus 20 menus in a scrolled box.

Not tested: Input with this build, and Chrome, Safari or Firefox by hand.

Before and after, 20 closed menus in a scrolled box (Storybook)
main this PR
Floating wrappers 20 0
ResizeObserver 20 0
IntersectionObserver 20 0
scroll listeners 60 0
resize listeners 60 0
  • One menu open: 1 wrapper, 1 ResizeObserver, 1 IntersectionObserver, 3 scroll and 3 resize listeners. All back to 0 on close, right away; the wrapper still fades out for 100 ms.
  • Same numbers with and without usePortal.
  • Menu position matches main in every story: 10 px below the button, same edge, and it follows the button when the box scrolls.
  • Enter: opacity 0, 0.24, 0.86, 1 over 200 ms. Leave: still fading at 50 ms, removed by 200 ms.
  • Enter, Space and ArrowDown open with the first item active; Escape closes and puts focus back on the button.

Counted by wrapping ResizeObserver, IntersectionObserver and add/removeEventListener before page load, counting only calls that come from floating-ui.

🤖 Generated with Claude Code

The floating wrapper is now rendered only while the menu is open, so
floating-ui's autoUpdate no longer keeps scroll/resize listeners, a
ResizeObserver and an IntersectionObserver for every closed menu.
MenuItems is static so its content stays during the leave transition,
and useFloating positions with left/top so the slide-in transform on
the wrapper isn't overridden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@PhilReinking PhilReinking left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK at b86f03a, CI green.

Checked beyond the PR text, in headless Chromium and WebKit: closed menus create no observers or listeners, the open position matches main in all four stories, and the menu neither flashes on open nor jumps while it fades out. Switching menus, an outside click and rapid toggling all end at zero. aria-controls exists only while open. The spec fails on main and when the v-if is removed.

@PhilReinking
PhilReinking merged commit 262a68f into main Oct 11, 2026
1 check passed
@PhilReinking
PhilReinking deleted the fix/bits-208-menu-closed-tracking branch October 11, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant