<!-- GENERATED by scripts/build-llms.mjs from llms/overlays.md — do not edit this file. -->

# `lr-dropdown`

- **Import** `import '@aceshooting/lyra-ui/components/lr-dropdown.js';` (stable tag alias; registers the tag)
- **Class** `LyraDropdown`, also available unregistered from `@aceshooting/lyra-ui/components/overlays/overlay/dropdown.class.js`
- **Family** `components/overlays/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 13 parts, 11 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-dropdown`

The complete mapped action-menu component. The public element remains a Popover-style trigger plus
positioned popup shell; the shell is presentation-only, and its contained `lr-menu` is the sole
menu role/name owner. That menu provides the same interaction engine as standalone `lr-menu`, so
direct `lr-dropdown-item`/`lr-menu-item` children get roving focus, disabled skipping, type-ahead,
nested submenu keyboard/pointer intent, and focus return without a second public popup. A
consumer-supplied `lr-menu` in the default slot becomes that contained engine instead of being
wrapped in another menu. This supports both Web Awesome's direct-item shape and Shoelace's
consumer-menu shape.

The inner menu list owns scrolling within the popup's height limit, keeping a consumer menu's
header and footer visible. The outer `popup` and `content` parts allow overflow so nested submenus
remain clickable outside the parent menu in every supported browser. This works with either
submenu authoring shape, with or without `hoist` or an arrow; no overflow override is required.

An open lr-dropdown repositions when its effective host or inherited text direction changes,
preserving open state without emitting lifecycle events.

**Direct mapped items.** `<lr-dropdown-item>` is the Web Awesome-compatible name for the same row
implementation as `<lr-menu-item>`. Direct mapped items receive this dropdown's `size` and use its
contained roving-focus, type-ahead, disabled-skipping, selection, and submenu controller; a
consumer-supplied `<lr-menu>` uses that controller directly. This preserves both Web Awesome's
direct-item composition and Shoelace's consumer-menu composition. The canonical item properties,
methods, events, slots, parts, and theme variables are documented in the layout-family
`lr-menu` / `lr-menu-item` section.
The trigger always receives `aria-haspopup="menu"`. `popupRole` is narrowed to the invariant
`'menu'`; assigning another runtime value or authoring another `popup-role` value normalizes it
back to `menu` and never puts a dialog/menu role on the outer positioning shell. This matches Web
Awesome's fixed inner menu role and Shoelace's consumer-menu ownership rather than exposing a
Lyra-only role switch.

ArrowDown opens and focuses the first enabled item; ArrowUp opens and focuses the last enabled
item, whether the interaction owner is slotted or resolved through `for`.
The inherited `data-popover="close"` descendant action is also supported and follows the same
disabled/inert and nearest-owner rules described for `lr-popover`.

The generated menu uses the dropdown's presence-sensitive host `aria-label`, `accessibleLabel`, or
localized "Menu" fallback. A consumer-supplied menu keeps its own naming precedence: its host
`aria-label` (including an explicit empty value), then an explicit nondefault `label`, then the
dropdown fallback. Its `header` and `footer` slots remain rendered outside the inner
`role="menu"` list while contained, including after live slot changes; Tab can therefore reach
their controls without putting arbitrary content inside the menu role.

**Properties:**

- `open: boolean = false` (reflected), `placement: Placement = 'bottom-start'`,
  `distance: number = 0`, `skidding: number = 0`, and `for: string = ''` — the same positioning
  vocabulary as `lr-popover`, except the mapped dropdown sits flush against its trigger by default.
  An explicit distance still wins, and generic `lr-popover` keeps its own default of `8`.
- `size: LyraSize = 'm'` (reflected) — propagated to directly owned mapped items. Accepts the
  six-step Lyra ladder and `small`/`medium`/`large` aliases.
- `disabled: boolean = false` (reflected) — prevents pointer/keyboard/programmatic opening and
  dismisses an already-open dropdown when enabled. Initial `disabled` plus `open` markup or
  pre-upgrade property writes normalize closed regardless of assignment/attribute order.
- `stayOpenOnSelect: boolean = false` (attribute `stay-open-on-select`, reflected) — suppresses the
  default selection close for direct and nested selections.
- `hoist: boolean = false` (reflected) — uses viewport-fixed positioning; otherwise the popup uses
  the containing-block (`absolute`) strategy.
- `sync?: 'width'|'height'|'both'` (reflected) — copies the trigger dimension(s) onto the popup.
- `positioningStrategy: PlaceStrategy = 'absolute'` (attribute `positioning-strategy`, reflected) —
  see `<lr-popover>`. `hoist: boolean = false` is its retained exact alias
  (`hoist` ⇔ `positioning-strategy="fixed"`); writing either spelling updates the other, so the two
  attributes can never disagree. Prefer `positioning-strategy` in new code. `<lr-dropdown>` also
  inherits `<lr-popover>`'s `trigger`/`showDelay`/`hideDelay`/`hoverBridge`, and honors the
  cascading `--lr-positioning-strategy` custom property ahead of this mirrored `absolute` default
  when neither spelling is authored on the instance.
- `containingElement?: HTMLElement` (property only) — an external element that counts as inside for
  light-dismiss handling.
- `arrow`, `withoutArrow` (`without-arrow`), `arrowPlacement`, `arrowPadding`, and `accessibleLabel`
  (`aria-label`) are retained from `lr-popover` for existing Lyra consumers.
- `popupRole: 'menu'` (attribute `popup-role`) is the narrowed inherited surface. Dropdowns cannot
  be changed into dialogs; use `lr-popover popup-role="dialog"` for arbitrary dialog-like content.

**Methods:** `show(): Promise<void>` and
`hide(options?: { focusTrigger?: boolean }): Promise<void>` use the same cancelable before-events,
after-events, focus return, and settlement rules as `lr-popover`. `reposition(): void` immediately
recomputes placement after an imperative anchor/layout change. `focusOnTrigger(options?): void`
focuses the first assigned trigger, and `getMenu(): LyraMenu | null` returns the live generated or
consumer-supplied contained menu engine. `showAt()` remains available for Lyra's virtual-anchor
compatibility surface.

While a consumer-supplied menu is contained, the dropdown snapshots every integration field it owns
(`dropdownOpen`, owner/contained/role flags, stay-open policy, and size). Removing/swapping that menu or
disconnecting the dropdown restores the exact author values, so reuse outside this dropdown does
not retain hidden parent policy.

**Events:** `lr-select` is the single mapped selection path: cancelable, bubbling/composed, with
`detail: { item }` carrying the activated element. Preventing it keeps the complete submenu chain
open; `stay-open-on-select` applies the same default suppression declaratively. Nested selection is
not translated or re-emitted at each level, so a listener on `lr-dropdown` receives exactly one
event. `lr-show` (cancelable), `lr-after-show`, `lr-hide` (cancelable), and `lr-after-hide` retain
the Popover lifecycle; none fires for initial open markup.

Dropdown motion resolves `dropdown.show` / `dropdown.hide` through the public animation registry;
it retains the dropdown's `--show-duration` / `--hide-duration` defaults when an override supplies
only keyframes. Passing `null` disables motion without skipping the after-event or promise.

**Slots:** `trigger`; default (`lr-dropdown-item`/`lr-menu-item` rows, or one consumer-supplied
`lr-menu`; that menu may use its own `header`/`footer` regions). **CSS parts:** `trigger`;
`popup dialog popup__popup base base__popup panel` (all six tokens on the neutral positioned
popup, preserving the popover, Web Awesome and Shoelace wrapper names on the same node); `menu`
(the contained semantic/controller owner); `content body`; the retained optional
`arrow popup__arrow` token set; and the inherited `hover-bridge` — the invisible quad the positioner
clips across the `distance` gap between trigger and popup, rendered only while a `hover`-triggered
dropdown with `hover-bridge` set is open, so a pointer travelling from the trigger to the popup
never leaves both at once and the surface does not close underneath it. It paints nothing by
default; style it only to debug the travel region.

**Themeable custom properties:** `--show-duration` and `--hide-duration` (both default
`var(--lr-transition-fast)`), mapped `--max-width` and `--arrow-size`, plus retained
`--lr-overlay-max-inline-size` and `--lr-overlay-arrow-size` fallbacks. The popup surface is
`lr-popover`'s, so the whole overlay-surface family reaches it unchanged: `--lr-overlay-surface`,
`--lr-overlay-border`, `--lr-overlay-radius`, `--lr-overlay-shadow-anchored`, and the cascading
`--lr-positioning-strategy` override documented on `<lr-popover>`
(`llms/components/lr-popover.md`).

```html
<lr-dropdown aria-label="File actions" size="small">
  <button slot="trigger">Actions</button>
  <lr-dropdown-item value="rename"
    ><span slot="details">⌘R</span>Rename</lr-dropdown-item
  >
  <lr-dropdown-item>
    Share
    <lr-dropdown-item slot="submenu" value="email">Email</lr-dropdown-item>
    <lr-dropdown-item slot="submenu" value="link">Copy link</lr-dropdown-item>
  </lr-dropdown-item>
  <lr-dropdown-item value="delete" variant="danger">Delete</lr-dropdown-item>
</lr-dropdown>
<script type="module">
  document
    .querySelector("lr-dropdown")
    .addEventListener("lr-select", (event) => {
      console.log(event.detail.item.value);
    });
</script>
```
