Skip to content

[menu] Slip-click silently cancels opening: mouseup a few px outside the trigger closes the just-opened menu (2px tolerance, no time threshold) #5158

Description

@Emma-Alpha

Bug report

Current behavior

Menu.Trigger opens the menu on mousedown. A one-time document mouseup listener is then attached (see handleDocumentMouseUp in packages/react/src/menu/trigger/MenuTrigger.tsx): if the mouseup lands outside the trigger's bounds (+2px BOUNDARY_OFFSET) and outside the popup, the just-opened menu is immediately closed (cancelOpen).

As a result, an ordinary quick click whose pointer drifts a few pixels past the trigger's edge between press and release opens the menu for a few tens of milliseconds and then silently closes it. There is no time threshold — even a <100ms click is treated as a "drag off and release" cancel.

On small triggers (icon buttons / chevrons in the 12–24px range) this happens surprisingly often with fast mouse movement or trackpad taps. To the user, the menu appears to "flash" or simply not react.

Real-world impact: we ship Base UI menus in production, and our product analytics recorded 15 rage-click events from 8 distinct users in a single day, all landing on menu triggers — users clicking repeatedly because the menu kept cancelling itself.

(Aside: on 1.5.0, aria-expanded also stayed "false" on the trigger while the popup was open, which made this harder to diagnose from telemetry. That appears to be fixed in 1.6.0.)

Expected behavior

A quick click that starts on the trigger should open the menu and leave it open, even if the pointer drifts slightly past the trigger's edge before release. Possible directions, in order of preference:

  1. Treat a short press (e.g. released within ~200ms of the press, similar in spirit to the existing PATIENT_CLICK_THRESHOLD) as a click rather than a drag-cancel — native macOS menus behave this way.
  2. Or widen the tolerance well beyond 2px (e.g. 16–24px) — 2px is far below normal click jitter.
  3. Or make cancel-on-release-outside opt-out via a prop.

Note the same BOUNDARY_OFFSET = 2 pattern also exists in SelectTrigger.tsx and ComboboxTrigger.tsx, so those components likely share the behavior.

Reproducible example

Reproducible on the official docs demo at https://base-ui.com/react/components/menu (the "Song" hero demo):

  1. Press the mouse button down on the trigger — the menu opens.
  2. While holding, move the pointer just past the trigger's edge (3–10px is enough).
  3. Release.
  4. The menu silently closes.

I verified this with synthesized real input events (CDP Input.dispatchMouseEvent): mousedown at the trigger center → menu open (aria-expanded="true", popup mounted) → mousemove to 9px past the right edge → mouseup → menu closed. Also reproduced in a minimal <Menu.Root><Menu.Trigger>… app on 1.6.0 with a 4px overshoot.

No special code is needed — any default Menu reproduces it.

Base UI version

1.6.0 (also present in 1.5.0)

Which browser are you using?

Chrome (reproduced on Chrome 149 and Chromium via CDP)

Which OS are you using?

macOS

Additional context

The press → drag off → release = cancel behavior itself is presumably intentional (press-drag-release menu interaction). The problem is that it applies with no time threshold and an extremely tight 2px boundary, so ordinary fast clicks on small triggers get misclassified as drag-cancels. Rage-click telemetry from production suggests this hits real users noticeably often.

Metadata

Metadata

Assignees

No one assigned

    Labels

    component: menuChanges related to the menu component.has workaroundThere’s a bug, but users have a complete workaround, so no urgent fix or release is needed.

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions