---
outline: deep
---

# Dropdown

Dropdowns are used to present a list of actions or options in a floating menu anchored to a trigger button. Commonly used for overflow menus, contextual actions, and navigation.

**`<l-dropdown>`** — Custom Element · Shadow DOM

## Options

### Basic

Click the trigger to open the menu. Click outside or press <kbd>Escape</kbd> to close.

```html
<l-dropdown>
  <button
    slot="trigger"
    class="l-button"
  >
    Options
  </button>
  <l-dropdown-item value="edit">Edit</l-dropdown-item>
  <l-dropdown-item value="duplicate">Duplicate</l-dropdown-item>
  <l-dropdown-item value="archive">Archive</l-dropdown-item>
</l-dropdown>
```

### Disabled items

Add `disabled` to individual items to prevent selection.

```html
<l-dropdown>
  <button
    slot="trigger"
    class="l-button"
  >
    Actions
  </button>
  <l-dropdown-item value="edit">Edit</l-dropdown-item>
  <l-dropdown-item value="duplicate">Duplicate</l-dropdown-item>
  <l-dropdown-item disabled>Archive</l-dropdown-item>
  <l-dropdown-item value="delete">Delete</l-dropdown-item>
</l-dropdown>
```

### Checkbox items

Set `type="checkbox"` for toggleable items. The dropdown stays open when checking items.

```html
<l-dropdown>
  <button
    slot="trigger"
    class="l-button"
  >
    View columns
  </button>
  <l-dropdown-item
    type="checkbox"
    checked
    >Name</l-dropdown-item
  >
  <l-dropdown-item
    type="checkbox"
    checked
    >Email</l-dropdown-item
  >
  <l-dropdown-item type="checkbox">Phone</l-dropdown-item>
  <l-dropdown-item type="checkbox">Address</l-dropdown-item>
</l-dropdown>
```

### Section labels

Add `<l-dropdown-label>` to caption a group of items. It is non-interactive — keyboard navigation and typeahead skip it. Use `<l-divider>` between sections.

```html
<l-dropdown>
  <button
    slot="trigger"
    class="l-button"
  >
    View options
  </button>

  <l-dropdown-label>Layout</l-dropdown-label>
  <l-dropdown-item
    type="checkbox"
    checked
    >Compact</l-dropdown-item
  >
  <l-dropdown-item type="checkbox">Comfortable</l-dropdown-item>

  <l-divider></l-divider>

  <l-dropdown-label>Columns</l-dropdown-label>
  <l-dropdown-item
    type="checkbox"
    checked
    >Name</l-dropdown-item
  >
  <l-dropdown-item
    type="checkbox"
    checked
    >Email</l-dropdown-item
  >
  <l-dropdown-item type="checkbox">Phone</l-dropdown-item>
</l-dropdown>
```

### Submenus

Nest `<l-dropdown-item slot="submenu">` elements inside an item to create a submenu — at any depth. A parent item shows a chevron, opens on hover, click, <kbd>Enter</kbd> or <kbd>ArrowRight</kbd>, and closes with <kbd>ArrowLeft</kbd> or <kbd>Escape</kbd>. Selecting a nested item fires the same `select` event on the dropdown and closes the whole menu (checkbox items keep it open). Use `<hr slot="submenu">` for separators inside a submenu.

```html
<l-dropdown>
  <button
    slot="trigger"
    class="l-button"
  >
    Export
  </button>

  <l-dropdown-item>
    Documents
    <l-dropdown-item
      slot="submenu"
      value="pdf"
      >PDF</l-dropdown-item
    >
    <l-dropdown-item
      slot="submenu"
      value="docx"
      >Word Document</l-dropdown-item
    >
  </l-dropdown-item>

  <l-dropdown-item>
    Spreadsheets
    <l-dropdown-item slot="submenu">
      Excel Formats
      <l-dropdown-item
        slot="submenu"
        value="xlsx"
        >Excel (.xlsx)</l-dropdown-item
      >
      <l-dropdown-item
        slot="submenu"
        value="csv"
        >CSV (.csv)</l-dropdown-item
      >
    </l-dropdown-item>
    <l-dropdown-item
      slot="submenu"
      value="ods"
      >OpenDocument (.ods)</l-dropdown-item
    >
  </l-dropdown-item>

  <hr />

  <l-dropdown-item>
    Options
    <l-dropdown-item
      slot="submenu"
      type="checkbox"
      value="compress"
      >Compress files</l-dropdown-item
    >
    <l-dropdown-item
      slot="submenu"
      type="checkbox"
      checked
      value="metadata"
      >Include metadata</l-dropdown-item
    >
  </l-dropdown-item>
</l-dropdown>
```

### Placement

Set `placement` to control position. Default is `bottom-start`.

```html
<div class="flex gap-4">
  <l-dropdown placement="bottom-start">
    <button
      slot="trigger"
      class="l-button"
    >
      Bottom start
    </button>
    <l-dropdown-item>Option 1</l-dropdown-item>
    <l-dropdown-item>Option 2</l-dropdown-item>
    <l-dropdown-item>Option 3</l-dropdown-item>
  </l-dropdown>

  <l-dropdown placement="bottom-end">
    <button
      slot="trigger"
      class="l-button"
    >
      Bottom end
    </button>
    <l-dropdown-item>Option 1</l-dropdown-item>
    <l-dropdown-item>Option 2</l-dropdown-item>
    <l-dropdown-item>Option 3</l-dropdown-item>
  </l-dropdown>

  <l-dropdown placement="top-start">
    <button
      slot="trigger"
      class="l-button"
    >
      Top start
    </button>
    <l-dropdown-item>Option 1</l-dropdown-item>
    <l-dropdown-item>Option 2</l-dropdown-item>
    <l-dropdown-item>Option 3</l-dropdown-item>
  </l-dropdown>
</div>
```

### Min width

Set `min-width="trigger"` to floor the panel at the trigger's width — useful for select-like triggers (a date-range or filter button). The panel still grows with its content and stays matched if the trigger resizes while open.

```html
<l-dropdown min-width="trigger">
  <button
    slot="trigger"
    class="l-button"
  >
    1 Jan 2026 – 2 Jun 2026
    <l-icon name="lucide:chevron-down"></l-icon>
  </button>
  <l-dropdown-item value="month">This month</l-dropdown-item>
  <l-dropdown-item value="quarter">This quarter</l-dropdown-item>
  <l-dropdown-item value="year">This year</l-dropdown-item>
</l-dropdown>
```

### Disabled

Add `disabled` to prevent opening.

```html
<l-dropdown disabled>
  <button
    slot="trigger"
    class="l-button"
    disabled
  >
    Options
  </button>
  <l-dropdown-item value="edit">Edit</l-dropdown-item>
  <l-dropdown-item value="duplicate">Duplicate</l-dropdown-item>
</l-dropdown>
```

## Examples

### Account menu

Use the `header` slot for a profile row, the `prefix` slot on each `<l-dropdown-item>` for a leading icon, and `<l-divider>` between groups for section breaks — `<l-dropdown>` tightens slotted `<l-divider>` spacing automatically.

```html
<l-dropdown>
  <l-avatar
    slot="trigger"
    interactive
    name="Jane Cooper"
    aria-label="Account"
    style="--appearance: circle; --color: var(--color-purple-200)"
  ></l-avatar>

  <div
    slot="header"
    class="flex items-center gap-3 px-2 py-2"
  >
    <l-avatar
      name="Jane Cooper"
      style="--appearance: circle; --color: var(--color-purple-200)"
    ></l-avatar>
    <div class="flex flex-col">
      <span class="text-sm font-medium text-primary">jane.cooper@acme.com</span>
      <span class="text-xs text-secondary">Super admin</span>
    </div>
  </div>

  <l-divider></l-divider>

  <l-dropdown-item value="profile">
    <iconify-icon
      slot="prefix"
      icon="lucide:user-round-cog"
    ></iconify-icon>
    Manage profile
  </l-dropdown-item>
  <l-dropdown-item value="preferences">
    <iconify-icon
      slot="prefix"
      icon="lucide:settings"
    ></iconify-icon>
    Preferences
  </l-dropdown-item>
  <l-dropdown-item value="help">
    <iconify-icon
      slot="prefix"
      icon="lucide:life-buoy"
    ></iconify-icon>
    Get help
  </l-dropdown-item>

  <l-divider></l-divider>

  <l-dropdown-item value="signout">
    <iconify-icon
      slot="prefix"
      icon="lucide:log-out"
    ></iconify-icon>
    Sign out
  </l-dropdown-item>

  <l-divider></l-divider>

  <div
    slot="footer"
    class="flex items-center justify-between gap-2 px-2 py-1.5 text-xs text-secondary"
  >
    <span>v1.2.3</span>
    <a
      class="hover:underline"
      href="#"
      >Documentation</a
    >
  </div>
</l-dropdown>
```

## Accessibility

### Criteria

- **Role** — Trigger is a menu button (`aria-haspopup="menu"`); panel has `role="menu"`, items have `role="menuitem"` or `role="menuitemcheckbox"`
- **Accessible name** — Each `role="menu"` panel is named — the root menu after its trigger, every submenu after its parent item
- **Expanded state** — Trigger receives `aria-expanded` reflecting open state
- **Checked state** — Checkbox items use `aria-checked` to communicate toggle state
- **Submenu state** — Items with a submenu expose `aria-haspopup="menu"`, `aria-controls`, and reflect `aria-expanded`
- **Disabled state** — Disabled items use `aria-disabled`, remaining in the DOM for discoverability
- **Focus management** — Focus moves into menu on open and returns to trigger on close; Tab closes the menu
- **Focus visible** — The focused item shows a focus ring distinct from the hover state
- **Motion** — Respects `prefers-reduced-motion`

### Rules

- Use a `<button>` element for the trigger in the `trigger` slot
- Every item must have visible text content for its accessible name
- For checkbox items, set `type="checkbox"` — the component handles `aria-checked` automatically

### Keyboard interactions

- `Enter` — Opens menu and focuses first item; selects the focused item; or opens the focused item's submenu
- `Space` — Opens menu and focuses first item; selects the focused item; or opens the focused item's submenu
- `ArrowDown` — Opens menu and focuses first item; or moves focus to the next item of the current level (wraps)
- `ArrowUp` — Opens menu and focuses last item; or moves focus to the previous item of the current level (wraps)
- `ArrowRight` — Opens the focused item's submenu and focuses its first item
- `ArrowLeft` — Closes the current submenu and returns focus to its parent item
- `Home` — Moves focus to the first item of the current level
- `End` — Moves focus to the last item of the current level
- `Escape` — Closes the current submenu; at the root level, closes the menu and returns focus to the trigger
- `Tab` — Closes menu and moves focus to the next focusable element

## API reference

### Importing

```js
import 'luxen-ui/dropdown';
import 'luxen-ui/dropdown-item';
import 'luxen-ui/dropdown-label';
```

### Attributes & Properties

- **open**: `boolean` (default: `false`) — Whether the dropdown is open.
- **placement**: `Placement` (default: `'bottom-start'`) — Preferred placement of the panel.
- **distance**: `number` (default: `4`) — Distance in pixels from the trigger.
- **disabled**: `boolean` (default: `false`) — Disables the dropdown trigger.
- **min-width**: `'trigger' | undefined` — Floor the panel's width at the trigger's width. Set to `trigger` so the
panel is never narrower than the trigger; it still grows with its content.
Useful for select-like triggers (a date-range or filter button) where the
panel should line up with the control. Re-applies if the trigger resizes
while open.

### Methods

- **show()**
- **hide()**
- **toggle()**

### Events

- **show** (cancelable) — Fired before the dropdown opens. Cancelable.
- **after-show** — Fired after the open animation completes.
- **hide** (cancelable) — Fired before the dropdown closes. Cancelable.
- **after-hide** — Fired after the close animation completes.
- **select** — Fired when an item is selected, including items nested in submenus. Bubbles. Properties: `item: DropdownItem`.

### Slots

- **trigger** — The element that triggers the dropdown.
- **header** — Optional content rendered above the menu items (e.g. a user profile row). Use an `<l-divider>` (or `<hr>`) after it to separate from items.
- **(default)** — Menu content (`l-dropdown-item` elements). Drop an `<l-divider>` (or `<hr>`) between items to render a section separator, or an `<l-dropdown-label>` to caption a group of items. Nest items with `slot="submenu"` inside an item to create a submenu.
- **footer** — Optional content rendered below the menu items (e.g. a version label or shortcut row). Use an `<l-divider>` (or `<hr>`) before it to separate from items.

### CSS custom properties

- `--background` — Panel background color.
- `--border-radius` (default: `8px`) — Panel border radius.
- `--padding` (default: `0.25rem`) — Panel inner padding. Slotted `<l-divider>` elements bleed by this amount on each side to span the panel edges.
- `--shadow` — Panel box shadow.
- `--show-duration` (default: `150`) — Show animation duration in ms.
- `--hide-duration` (default: `150`) — Hide animation duration in ms.

### `dropdown-item` Attributes & Properties

- **value**: `string` — The value associated with this item.
- **disabled**: `boolean` (default: `false`) — Disables the item.
- **type**: `'normal' | 'checkbox'` (default: `'normal'`) — The type of item: `normal` or `checkbox`.
- **checked**: `boolean` (default: `false`) — Whether the checkbox item is checked.
- **submenu-open**: `boolean` (default: `false`) — Whether this item's submenu is open. Managed by the parent `l-dropdown`.
- **hasSubmenu**: `boolean` — Whether this item has nested `slot="submenu"` items.

### `dropdown-item` Slots

- **(default)** — Label text.
- **prefix** — Leading content (e.g. icon).
- **suffix** — Trailing content.
- **submenu** — Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.

### `dropdown-label` Slots

- **(default)** — Label text.

### `dropdown-label` CSS custom properties

- `--color` (default: `var(--l-color-text-tertiary)`) — Text color.
