MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

Menus

Menus display a list of choices on a temporary surface. The MD3 Expressive Menu supports shape morphing, selection states, nested submenus, and vibrant color variants.

Menus appear upon interaction with a button, action, or other control. They display a list of choices, one per line. The MD3 Expressive system introduces sophisticated motion, shape morphing, and high-fidelity color tokens to traditional M3 menus.

Introduction

The MD3 Expressive Menu is a high-density navigation and action component. It supports two design variants: Baseline (standard MD3) and Expressive (dynamic morphing). Built on top of Radix UI's Menu primitives, it ensures perfect positioning, keyboard navigation, and accessibility while providing the premium "Expressive" look and feel unique to this library.

Anatomy

  • Trigger: The element (e.g., Button, Icon) that opens the menu.
  • Content Panel: The floating surface holding the menu items.
  • Menu Item: An individual choice. Can include leading icons, trailing text (shortcuts), and supporting text.
  • Submenu: A nested menu that opens from a parent menu item.
  • Divider / Gap: Visual separators between groups of related items.
  • Active Indicator: Marks a selected item, often with a checkmark and shape morphing.

Variants

Design Variants

  • Baseline: Standard Material Design 3 look. Clean, predictable, and familiar.
  • Expressive: Features fluid shape morphing and scaling transitions. Items "bloom" into view with spring physics.
Loading demo...

Color Variants

  • Standard: Uses the Surface Container roles.
  • Vibrant: Uses Tertiary Container roles for high emphasis. Exclusive to Vertical menus.
Loading demo...

Features

Selection States

Menus support single or multiple selection states. In the Expressive variant, selected items feature enhanced visual feedback and shape morphing.

Loading demo...

For complex hierarchical actions, use nested Submenus. The system handles positioning and focus management automatically.

Loading demo...

Icons & Shortcuts

Reinforce meaning with leadingIcon and provide efficiency with trailingText (shortcuts like "⌘C").

Loading demo...

Select & Autocomplete (Exposed Dropdown)

The <Select /> component implements the Material Design 3 Expressive Exposed Dropdown Menu spec. It combines a TextField (Outlined or Filled) with a Menu popover, supporting both standard selection and real-time Autocomplete filtering (searchable={true}).

Standard Select

A clean single-select dropdown with floating label and animated trailing arrow.

Loading demo...

Autocomplete Mode

Set searchable={true} to transform the Select into a real-time searchable input field that filters options dynamically as the user types.

Loading demo...

Outlined vs Filled Variants

<Select /> supports all TextField styling variants (outlined | filled), supporting text, and option leading icons.

Loading demo...

Divider & Separator Customization

Control whether horizontal dividers appear between dropdown options with showDividers={false} or specify separatorStyle="none" | "divider" | "gap".

Loading demo...

Async Autocomplete & Loading State

For remote APIs, combine searchable, onSearchChange, and loading to display a built-in progress indicator while fetching results asynchronously.

Loading demo...

Large Datasets & Virtualization

Progressive chunk loading via maxVisible and IntersectionObserver enables effortless scrolling and searching across 1,000+ items without UI stutter.

Loading demo...

Usage

Basic Usage

import { 
  Menu, 
  MenuTrigger, 
  MenuContent, 
  MenuItem, 
  Button,
  Select
} from "@bug-on/m3-expressive";

export function Example() {
  return (
    <Menu>
      <MenuTrigger asChild>
        <Button>Open Menu</Button>
      </MenuTrigger>
      <MenuContent>
        <MenuItem leadingIcon={<Icon name="edit" />}>Edit</MenuItem>
        <MenuItem leadingIcon={<Icon name="share" />}>Share</MenuItem>
        <MenuItem leadingIcon={<Icon name="delete" />} disabled>Delete</MenuItem>
      </MenuContent>
    </Menu>
  );
}

Select Usage

import { Select } from "@bug-on/m3-expressive";

export function SelectExample() {
  const [state, setState] = React.useState("ca");

  return (
    <Select
      label="State / Province"
      options={[
        { label: "California", value: "ca" },
        { label: "New York", value: "ny" }
      ]}
      value={state}
      onChange={(val) => setState(val)}
      showDividers={false}
    />
  );
}
<Select
  searchable
  label="Search employees"
  placeholder="Type a name..."
  options={employeesList}
  onChange={(val, option) => setSelected(option)}
  emptyText="No employees found"
/>

Async Autocomplete Usage (Remote / Server-side)

<Select
  searchable
  loading={isLoading}
  label="Search Countries"
  placeholder="Type country name..."
  options={searchResults}
  onSearchChange={(query) => fetchCountriesFromAPI(query)}
  onChange={(val, option) => setSelectedCountry(option)}
/>

Best Practices

Do

  • Use menus for a list of related actions that are hidden by default to save space.
  • Use icons to help users scan the list more quickly.
  • Group related items using dividers or gaps.
  • Ensure shortcuts (trailing text) follow system-standard conventions.
  • Use <Select searchable /> when the list contains more than 7-10 options to enable fast filtering.
  • Use showDividers={false} for shorter, cleaner dropdowns where items do not need explicit separators.

Don't

  • Don't use a menu for only one item; use a single button instead.
  • Don't create deeply nested submenus (more than 2 levels); it becomes difficult to navigate.
  • Avoid putting too much content in a single menu item (keep labels short).

Accessibility

  • Keyboard: Full support for Arrow keys, Home/End, Escape, and character search.
  • ARIA: Automatically applies role="menu", role="menuitem", and aria-haspopup.
  • Focus: Focus is trapped within the menu and returned to the trigger on close.
  • Screen Readers: Supports supportingText which is read alongside the item label.

API Reference

Root provider.

PropTypeDefaultDescription
menuVariant"baseline" | "expressive""baseline"Design style.
colorVariant"standard" | "vibrant""standard"Color role.
openbooleanControlled open state.
onOpenChange(open: boolean) => voidChange callback.
PropTypeDefaultDescription
leadingIconReactNodeIcon at the start.
trailingTextstringShortcut (e.g., "⌘C").
selectedbooleanfalseShows active state.
supportingTextReactNodeHelper text below label.
disabledbooleanfalseBlocks interaction.
onClick() => voidClick handler.

Select

PropTypeDefaultDescription
optionsSelectOption[]RequiredArray of selectable options.
valuestringControlled selected value.
defaultValuestringUncontrolled initial value.
onChange(value: string, option?: SelectOption) => voidCallback when selection changes.
onSearchChange(query: string) => voidCallback for remote/async search. Bypasses internal client filtering.
searchablebooleanfalseEnables Autocomplete mode with real-time text filtering.
loadingbooleanfalseDisplays a loading spinner inside the dropdown during async fetch.
showDividersbooleantrueWhether to show horizontal divider lines between options.
separatorStyle"divider" | "gap" | "none""divider"Fine-grained separator appearance between menu options.
maxVisiblenumber50Maximum items rendered per chunk in virtual scroll window.
variant"outlined" | "filled""outlined"TextField border style.
labelstringFloating input label text.
placeholderstringInput placeholder.
supportingTextstringHelper text shown below the input.
colorVariant"standard" | "vibrant""standard"Dropdown menu color theme.
menuVariant"expressive" | "baseline""expressive"Visual design style of dropdown menu.
matchTriggerWidthbooleantrueWhen true, popover width automatically matches input width.
emptyTextstring"No options found"Message displayed when searchable filter yields no results.
renderOption(option, state) => ReactNodeCustom renderer for individual option items.
renderContent(state) => ReactNodeCustom renderer for complete dropdown menu content.
disabledbooleanfalseDisables the select input and menu.

SelectOption

FieldTypeRequiredDescription
labelstringYesDisplay text for the option.
valuestringYesUnique string identifier.
disabledbooleanNoDisables selection for this option.
leadingIconReactNodeNoIcon rendered at start of menu item.
supportingTextReactNodeNoSecondary helper text below option label.