Skip to content

DropdownMenu / MoreMenu: first item looks selected when opened by pointer (no way to opt out of initial focus) #4477

Description

@ShashankSirmour

Summary

When a DropdownMenu (or MoreMenu) is opened with a mouse click, focus moves to the first enabled item and its focus ring renders. To users it reads as "this item is already selected" — several people on my team have asked whether the first action was pre-chosen or somehow highlighted by mistake.

For keyboard opens this is exactly right and shouldn't change. The problem is only that pointer opens get the same treatment, and there's no prop to opt out.

What I ran into

A table row actions menu — Edit details / Edit permissions / Delete role:

<DropdownMenu
  button={{ label: `Actions for ${role.name}`, icon: <MoreVertical />, isIconOnly: true, variant: 'ghost' }}
  hasChevron={false}
>
  <DropdownMenuItem icon={Pencil} label="Edit details" onClick={...} />
  <DropdownMenuItem icon={SlidersHorizontal} label="Edit permissions" onClick={...} />
  <DropdownMenuItem icon={<Trash2 />} label={<span>Delete role</span>} onClick={...} />
</DropdownMenu>

Click the three-dot trigger → "Edit details" is visibly highlighted before the pointer goes anywhere near it. It also shifts depending on state: when the first item is isDisabled, the highlight lands on whichever item is first enabled, so the "selected" row appears to move around between rows in the same table.

Same behavior via the items data prop and via MoreMenu, so it looks like it's in the shared keyboard/focus path rather than either wrapper.

Why it's awkward to work around

DropdownMenuProps exposes button, isMenuOpen, onOpenChange, menuWidth, onClick, hasChevron, placement, data-testid — nothing focus-related. DropdownMenuItemProps has icon, label, description, onClick, isDisabled, endContent. So there's no supported way to say "don't focus anything until the user arrows in", short of CSS against internals, which I'd rather not ship.

Proposals (either would solve it)

A. Match focus behavior to the input modality (my preference — no new API, fixes it everywhere at once)
Focus the first item when the menu is opened via keyboard (Enter / Space / ArrowDown on the trigger), but on pointer open leave focus on the menu container so nothing is visually highlighted. First ArrowDown then moves to item 1. This is what most menu implementations do, and it pairs naturally with :focus-visible.

B. Add an opt-out prop

<DropdownMenu hasInitialFocus={false}> ... </DropdownMenu>

Defaults to true so nothing changes for existing users.

If the highlight is intended to be :focus-visible-driven already, then this may just be a bug where the ring shows on programmatic focus after a pointer open.

Acceptance criteria

  • Opening by mouse click shows no item highlighted; the menu is still keyboard-operable and ArrowDown moves to the first item.
  • Opening by keyboard still focuses the first enabled item immediately (unchanged).
  • Behavior is the same for DropdownMenu items-array mode, DropdownMenu compound mode, and MoreMenu.
  • Disabled leading items are still skipped when focus does move.
  • Screen reader announcement of the menu and its items is unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions