Skip to main content

Dropdown

An accessible dropdown menu component for displaying contextual actions or navigation options. Supports full keyboard navigation, focus management, and portal-based rendering.


Usage

Basic usage

import {
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
} from '@nofinite/nui';

<Dropdown>
<DropdownTrigger>Menu</DropdownTrigger>

<DropdownMenu>
<DropdownItem>Profile</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownItem>Logout</DropdownItem>
</DropdownMenu>
</Dropdown>;

With interaction handlers

<Dropdown>
<DropdownTrigger>Actions</DropdownTrigger>

<DropdownMenu>
<DropdownItem onSelect={() => console.log('Edit')}>Edit</DropdownItem>
<DropdownItem onSelect={() => console.log('Delete')}>Delete</DropdownItem>
</DropdownMenu>
</Dropdown>

Variants

This dropdown does not expose explicit visual variants via props.

Styling approach:

  • Use className on DropdownTrigger, DropdownMenu, or DropdownItem
  • Theme is controlled via CSS variables
<DropdownTrigger className="my-trigger" />
<DropdownMenu className="my-menu" />
<DropdownItem className="my-item" />

Sizes

No built-in size prop is provided.

Sizing is controlled via CSS:

  • Padding
  • Font size
  • Min-width
.ui-dropdown-menu {
min-width: 200px;
}

States

Open / Closed

  • Controlled internally via trigger click

  • Automatically closes on:

    • Click outside
    • Escape key
    • Item selection

Focus States

  • Focus moves to first item when menu opens
  • Focus is restored to trigger when menu closes

Native Props / Composition

Trigger

DropdownTrigger renders a native <button> and supports standard button attributes.

<DropdownTrigger aria-label="Open menu" onClick={customHandler}>
Menu
</DropdownTrigger>

DropdownItem renders a focusable element with role="menuitem".

<DropdownItem data-testid="logout-item">Logout</DropdownItem>

Props

PropTypeDefaultDescription
childrenReactNodeDropdown trigger and menu components
PropTypeDefaultDescription
childrenReactNodeTrigger content
classNamestring""Custom CSS class names
PropTypeDefaultDescription
childrenReactNodeDropdown items
classNamestring""Custom CSS class names
PropTypeDefaultDescription
childrenReactNodeItem content
onSelect() => voidCalled when item is selected
classNamestring""Custom CSS class names

Behavior Notes

  • Uses roving tabindex for arrow-key navigation
  • Arrow keys move focus between items
  • Enter / Space triggers item selection
  • Escape closes the menu
  • Clicking an item automatically closes the menu
  • Menu is rendered in a portal to avoid overflow issues
<DropdownItem onSelect={handleSelect} />

Accessibility

  • Trigger:

    • Renders as <button>
    • Uses aria-haspopup="menu"
    • Uses aria-expanded
  • Menu:

    • role="menu"
    • Keyboard navigable (↑ ↓ Enter Esc)
  • Menu Item:

    • role="menuitem"
    • Programmatically focusable
    • WCAG-compliant focus styles

Recommended for icon-only triggers:

<DropdownTrigger aria-label="Open options">
<IconMore />
</DropdownTrigger>

Layout

  • Dropdown root is inline-block
  • Menu is absolutely positioned via portal
  • Does not affect document flow
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Dropdown>
<DropdownTrigger>Menu</DropdownTrigger>
<DropdownMenu>...</DropdownMenu>
</Dropdown>
</div>

Best Practices

Do

  • Use for short, contextual actions
  • Keep item labels concise
  • Group related actions together
  • Use keyboard navigation as primary UX reference

Don’t

  • Place complex forms inside menu items
  • Use for large navigation menus
  • Nest dropdowns inside dropdown items
  • Rely on hover-only interactions