Skip to content

Dropdown ​

A click-triggered menu that opens anchored to the trigger element.

Basic usage ​

Trigger on click

Actions

Trigger ​

The trigger attribute controls how the menu opens: click (default) / hover / focus, multi-selectable with spaces (e.g. "click hover"). With hover, hover-delay / hover-hide-delay debounce open/close (default 150 / 100ms — without a delay hover flickers). The hover area is the trigger plus the floating panel (moving across the gap does not close it).

Hover trigger

Hover to open

Focus trigger

Focus to open (Tab or click)

Multiple triggers (click + hover)

Click or hover

Custom hover delays

hover-delay=400hover-hide-delay=400

Touchscreen degradation (automatic on coarse pointers) ​

Touch devices (pointer: coarse, e.g. phones and tablets) have no stable hover semantics: a tap synthesizes both hover and click events, which would open-then-immediately-close the menu with the desktop behavior. When trigger includes hover, the hover channel is automatically disabled on coarse pointers and degrades to tap toggling — tap the trigger to open, tap again to close, or tap outside the menu to dismiss (outside dismiss replaces hover-out). Fine-pointer (desktop mouse) behavior is unchanged; compatible with controlled open.

Tap toggling on touch (automatic on touch devices)

Tap me on touch

Placement ​

Four directions

UpDownLeftRight

12-way placement ​

placement supports 12 directions: each of the four bases (top / bottom / left / right) pairs with a cross-axis -start / -end align (bottom-start — the panel's left edge aligns with the trigger's left edge — is the most common form). When the panel flips along the main axis due to lack of space, the align suffix is preserved (bottom-start → top-start), and the aligned position is still clamped to the viewport.

12-way placement

bottom-startbottom-endright-starttop-end

Nested submenus ​

items entries support a children array for cascading submenus (any depth); hover/click expands, selecting a leaf collapses and closes. The floating menu reuses oas-menu, so nested submenus automatically flip left/up near viewport edges to stay fully visible.

Nested submenus

More actions

Disabled items ​

Disabled items

Actions

Disabled dropdown ​

disabled disables the whole dropdown: click / hover / focus triggers are ignored, the split arrow button is disabled, the host is desaturated (opacity .6) and syncs aria-disabled.

Disabled dropdown

Disabled (click)Disabled (split)

Arrow ​

The menu panel shows an arrow pointing at the trigger element . The arrow attribute toggles it (shown by default; arrow="false" hides it). arrow-point-at-center pins the arrow to the panel center (by default the arrow follows the trigger's projection, so it still points at the trigger even after viewport-avoidance shifting). auto-adjust-overflow controls whether the placement auto-flips when there is not enough viewport space (enabled by default).

With arrow

With arrow

Arrow visibility

Default (arrow)arrow="false"

Arrow at center & no overflow adjust

arrow-point-at-centerauto-adjust-overflow="false"

Selection event ​

Selection event

Select an actionNothing selected

Keep open after select ​

hide-on-click controls whether selecting a menu item closes the menu (default true — close on select; "false" keeps it open, useful for multi-select / check scenarios).

Keep open after select

Keep openNothing selected

Controlled display ​

The open attribute is controlled: an external button can set/remove open to show/hide the menu (clicking outside / pressing Esc / selecting an item still closes it).

Controlled display (open attribute)

OpenCloseopen: falseTrigger element

Open-change event ​

The component fires oas-open-change (detail: { open }) whenever it opens or closes itself (click / hover / focus / Esc / outside click / select) — the host can sync state in controlled mode (same semantics as tooltip / popover; externally writing open also triggers it).

oas-open-change event

Click or hoveropen: false

Offset & scroll ​

offset adjusts the gap between the panel and the trigger (default 8px). When the page scrolls, the panel repositions to follow the trigger (fixing the fixed-positioning detachment); close-on-scroll closes it on scroll instead.

Offset

Default (8px)offset=16

Close on scroll

Open then scroll the page

Controlled selection ​

The value attribute is controlled: an external value sets the selected item (the dropdown shows no check mark, so a tag echoes the current value in real time); selecting a menu item also updates value and fires oas-select.

Controlled selection (value attribute)

Select "Edit"Select "Copy"Clearvalue: -Select an action

Split button ​

The split attribute turns the dropdown into a split button : a main button plus a separate arrow button. Click the arrow to open the menu; clicking the main button fires oas-action (bind your primary action, e.g. save); selecting a menu item still fires oas-select.

Split button

Save & submit

Main button action event

More actionsNot clicked yet

Loading menu items ​

A menu item with loading: true enters a loading state: it shows a spinning indicator and is not selectable (click / keyboard / hover are all ignored); update items to remove loading once the async work finishes. The demo below marks "Save" as loading for ~1.5s after you select it.

Loading menu items

Actions

Async operation demo

Select an actionNothing selected

Declarative child-element channel ​

Besides the items JSON, you can declare the menu with <oas-dropdown-item> / <oas-dropdown-group> / <oas-dropdown-divider> child elements (the items attribute wins when set explicitly; otherwise children are parsed and converge to the same rendering path). Default slot text becomes the label; attributes map to the items fields: value / disabled / loading / icon / kind / danger / href / target / rel. Nested elements inside an <oas-dropdown-item> become cascading submenus recursively; an <oas-dropdown-group>'s label is the group title (value can act as a radio-group id) and its children flatten to the same level. Adding/removing children or changing their attributes/text re-renders automatically (MutationObserver).

Declarative children (group / divider / nested / checkbox / danger / href)

ActionsHomeComponent docsEdit CopyCutShow gridlinesDeleteNothing selected

Dynamic add/remove (MutationObserver auto refresh)

Append an itemDynamic menuHomeSettings

Leading media (slot="leading") ​

Menu items support a leading media slot: put a <template slot="leading"> (or a direct child with slot="leading") inside an <oas-dropdown-item> (shared with <oas-menu-item>); the content renders before the label and takes precedence over the icon attribute (when both are given the icon is not rendered); in the loading state the spinner replaces the leading media. Suited to avatars, color chips, and any rich leading content (items JSON does not support it — declarative children only).

Leading avatars (avatar example)

Switch accountLin XiaoyuChen YiningZhao QimingSign out

Trigger size and type ​

size (five tiers xs / small / medium / large / xl, sm/md/lg aliases accepted) and type (aligned with the oas-button type vocabulary) are forwarded to the trigger oas-button (a direct child). Dimensions not set on the dropdown keep the trigger button's own attributes; forwarding silently no-ops when the trigger is not an oas-button. Invalid size falls back to medium with a one-time warning.

size passthrough (five tiers)

xssmallmediumlargexl

type passthrough (aligned with button type)

primary triggerdanger trigger

Panel max height ​

max-height caps the menu panel height (numbers are px; forwarded to the inner oas-menu channel) — the panel scrolls internally, for long menus.

max-height=120 (panel scrolls internally)

Long menu (120px cap)

Reserve Widest Option (reserve-width) ​

reserve-width: no jitter when switching (open the dropdowns to compare the two buttons)

AlignmentControl (no reserve)

reserve-width (opt-in) measures the max text width of all menu item labels and writes it to the trigger min-width: the trigger never jitters when switching values (high-frequency toolbar/filter switching). Both the items attribute and the declarative child channel participate; content changes re-measure automatically; removing the attribute restores the trigger width. The measurement is an approximation (trigger chrome margin tunable via the --oas-dropdown-reserve-pad variable, default 40px). The first button above has the reserve on (sized by the widest of the three options), the second is the control; open the menus and switch values to compare.

API ​

oas-dropdown ​

Attributes ​

AttributeDescriptionTypeDefault
arrowWhether to show the arrow pointing at the trigger (arrow="false" hides it; the skeleton is kept)stringtrue
arrow-point-at-centerPin the arrow to the panel center (by default the arrow follows the trigger's projection, so it still points at the trigger even after viewport-avoidance shifting)boolean—
auto-adjust-overflowAuto-flip/avoid when there is not enough viewport space (auto-adjust-overflow="false" disables it; the panel may overflow the viewport)stringtrue
close-on-scrollClose the popover on page scroll (default repositions to follow; true closes on scroll)boolean—
disabledDisable entirely: no trigger responseboolean—
hide-on-clickClose after selecting an item (default true; false keeps it open for multi-select)stringtrue
hover-delayOpen delay in ms on hover trigger (default 150)——
hover-hide-delayClose delay in ms on hover trigger (default 100)——
itemsMenu items JSONstring[]
max-heightCaps the menu panel height with internal scrolling (numbers get px)string—
offsetGap in px between popover and trigger (default 8)——
openControlled display (boolean attribute; expands when present)boolean—
placementPopup placementstringbottom
reserve-widthReserve the widest option width on the trigger (opt-in): measures the max text width of all menu item labels and writes it to the trigger min-width so switching values never jitters; re-measured automatically when items/children change; removing the attribute restores the trigger widthboolean—
sizeForwarded to the trigger oas-button size tiers (aliases normalized, invalid falls back to medium)string—
splitSplit button mode (boolean attribute): main button + arrow button; arrow opens the menu, main button fires oas-actionboolean—
triggerTrigger: click (default) / hover / focus; space-separated for multiple (e.g. "click hover"). With hover, touch devices (coarse pointer) automatically degrade to tap toggling: tap to open, tap again or tap outside to close; desktop hover behavior unchangedstringclick
typeForwarded to the trigger oas-button type vocabularystring—
valueCurrent selected valuestring—

Events ​

EventDescription
oas-actionMain button clicked in split mode, detail: { originalEvent }
oas-open-changePopover open state changed, detail: { open: boolean } (including external setAttribute; controlled loop)
oas-selectAn item was selected, detail: { value }

Slots ​

NameDescription
defaultDropdown menu content (items / groups)

CSS Variables ​

CSS VariableDefault
--oas-dropdown-arrow-height12px
--oas-dropdown-arrow-radius0
--oas-dropdown-arrow-width12px
--oas-glass-blurnone
--oas-glass-ringtransparent
--oas-origin-xcenter
--oas-origin-ycenter

oas-dropdown-item ​

Attributes ​

AttributeDescriptionTypeDefault
dangerDestructive item: red semantics (delete/exit etc.)——
disabledDisable this item——
hrefLink address: with href the item renders as a native <a> (real navigation + still emits oas-select)——
iconLeading icon (icon name from the @oas-ui/icons registry)——
icon-colorIcon color: fixes the icon to this color (overrides the selected/disabled default); defaults to currentColor following the text color (only effective via the items JSON channel; the child-element channel does not parse this attribute)——
kindLeaf semantics: radio (default, selectable) / action (no checked state, does not write back value) / checkbox (multi-select, value is the checked-set array)——
loadingLoading: renders a spinner and blocks clicks; recovers when data-driven——
relLink rel (with href)——
shortcutShortcut hint rendered as a trailing kbd (aligned with the menubar/menu shortcut contract) (only effective via the items JSON channel; the child-element channel does not parse this attribute)——
targetLink target (with href)——
valueSelected value (data-carrier field of the declarative child-element channel)——

Slots ​

NameDescription
defaultDropdown item label content (default slot text); direct child <oas-dropdown-item> elements recursively become the submenu children
template[slot="leading"]Leading media template (or a direct child with slot="leading"): avatars/color chips/any rich leading content, rendered before the label and taking precedence over the icon attribute; replaced by the spinner in the loading state; declarative child channel only (not supported by the items JSON)

oas-dropdown-group ​

Attributes ​

AttributeDescriptionTypeDefault
labelGroup title (small, secondary color, not clickable)——
valueRadio-group id (selecting inside the group only updates that group's value)——

Slots ​

NameDescription
defaultGroup items: child <oas-dropdown-item> elements flatten to the same level

oas-dropdown-divider ​

Slots ​

NameDescription
defaultDivider data carrier (no attributes; the host parses it as type: "divider")

Clicking the trigger toggles visibility; clicking outside / pressing Esc / selecting an item closes it. The floating menu is an inner oas-menu (role="menu", leaf items menuitemradio, items with submenus menuitem) supporting cascading submenus and keyboard navigation.

Released under the MIT OR Apache-2.0 License.