Component

<dom-dropdown>

custom element

A menu that opens from a button. The menu panel uses the same native Popover API as dom-popover (top layer, light-dismiss, Esc), plus menu keyboard navigation and dom:select.

Demo

Account menu

Menu items use role="menuitem" and data-value — selection emits dom:select.

basic.htmlhtml
<dom-dropdown>
	<button
		slot="trigger"
		type="button"
		class="inline-flex h-10 items-center gap-2 rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border hover:bg-accent"
	>
		Account ▾
	</button>
	<div slot="menu" class="dom-dropdown-menu min-w-[10rem] rounded-2xl border border-border bg-background p-1 shadow-lg">
		<button role="menuitem" data-value="profile" class="block w-full rounded-xl px-3 py-2 text-left text-sm text-fg hover:bg-secondary">Profile</button>
		<button role="menuitem" data-value="billing" class="block w-full rounded-xl px-3 py-2 text-left text-sm text-fg hover:bg-secondary">Billing</button>
		<button role="menuitem" data-value="signout" class="block w-full rounded-xl px-3 py-2 text-left text-sm text-fg hover:bg-secondary">Sign out</button>
	</div>
</dom-dropdown>

<script type="module">
	import '@getdom/studio/headless/dropdown.js';
</script>

Usage

Plain HTML

<dom-dropdown>
  <button slot="trigger">Account ▾</button>
  <div slot="menu">
    <button role="menuitem" data-value="profile">Profile</button>
    <button role="menuitem" data-value="billing">Billing</button>
    <button role="menuitem" data-value="signout">Sign out</button>
  </div>
</dom-dropdown>

<script type="module">
  import '@getdom/studio/headless';
  document.querySelector('dom-dropdown')
    .addEventListener('dom:select', (e) => console.log(e.detail.value));
</script>

Register every <dom-*> in one import: import '@getdom/studio/headless'.

Slots

NameScopeDescription
#triggerButton that opens the menu.
#menuMenu panel (popover="auto"). Items inside must carry role="menuitem". data-value flows through dom:select.

Attributes

NameTypeDescription
openbooleanReflects open state (toggle by setting / removing).
placement'top' | 'right' | 'bottom' | 'left' | '<side>-<align>'Preferred placement before collision handling (default bottom).
align'left' | 'right'Horizontal alignment under the trigger (default left).
offsetnumberGap in pixels between trigger and menu (default 4).
collision-paddingnumberViewport padding used when flipping or shifting the menu (default 8).
floating-mode'viewport' | 'anchor'viewport keeps the menu inside the browser; anchor keeps it attached to the trigger while scrolling.
lock-scrollbooleanLock document scrolling while the menu is open.
data-menu-idstringId of an external (teleported) menu element. Used when the menu lives outside the host — e.g. portalled to <body>.

From dom-dropdown.__doc.attributes.

Events

NamePayloadDescription
dom:openFired when the menu opens.
dom:closeFired when the menu closes.
dom:select{ value, event }Fired when a menu item is chosen.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Keyboard

  • Enter / Space / ↓Open menu (when trigger is focused).
  • ↑ / ↓Move active item.
  • Home / EndJump to first / last item.
  • EnterSelect active item.
  • Esc / TabClose menu and return focus.

Related

See also

← Headless overview