ODropdown
ODropdown anchors reusable popup content to a native button without imposing selection state on that content.
Usage
Step by step
- Import
ODropdownfrom theooko-ui/dropdownpublic subpath. - Put the accessible trigger label in the default slot; the dropdown adds a Lucide direction icon from
placement. - Put content matching
popupRolein thecontentslot and use itsclosefunction after an action.
Example
The rendered example and its source come from the same Vue single-file component.
No action selected
Interactive playground
Change the trigger label, popup role, placement, controlled open state, and disabled state. The preview updates immediately.
No action selected
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls the popup when provided; omit it to let the dropdown manage its own state. |
trigger | boolean | true | Renders the built-in button trigger. Set to false when an owning control opens the popup itself. |
disabled | boolean | false | Prevents the trigger from opening the popup. |
placement | ODropdownPlacement | "bottom-start" | Positions the popup relative to its trigger. |
popupRole | ODropdownPopupRole | "menu" | Declares the popup's accessible role. |
popupLabel | string | undefined | Provides the popup's accessible name instead of deriving it from the trigger. |
glass | boolean | false | Uses a translucent, blurred popup with a thin border. Defaults to the app's glass option. |
Events
| Event | Payload | Description |
|---|---|---|
update:open | boolean | Requests or reports a change to the popup's open state. |
Slots
| Slot | Props | Description |
|---|---|---|
default | none | Provides the trigger label displayed before the automatic placement icon. |
trigger | none | Replaces the complete trigger content, including the automatic placement icon. |
content | ODropdownContentSlotProps | Provides the popup content and an explicit close action. |
Related types
| Type | Definition |
|---|---|
ODropdownContentSlotProps | interface ODropdownContentSlotProps { /** Closes the popup and optionally restores focus to its trigger. */ close: (restoreFocus?: boolean) => void; } |
ODropdownPlacement | type ODropdownPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end" | "right-start" | "right-end" | "left-start" | "left-end"; |
ODropdownPopupRole | type ODropdownPopupRole = "dialog" | "menu"; |
CSS custom properties
| Custom property | Default | Description |
|---|---|---|
--o-dropdown-panel-min-width | 10rem | Sets the popup's minimum width before viewport constraints apply. |
--o-dropdown-panel-padding | var(--o-space-2) | Sets the popup's internal spacing. |
--o-dropdown-panel-border | 1px solid var(--o-border-color-subtle) | Sets the popup border shorthand. |
--o-dropdown-panel-radius | var(--o-radius-surface) | Sets the popup corner radius. |
--o-dropdown-panel-background | var(--o-background-glass-strong) | Sets the popup background. |
--o-dropdown-panel-z-index | 10 | Sets the popup stacking order. |
--o-dropdown-glass-background | var(--o-background-glass) | Sets the translucent popup background of a glass dropdown or select. |
--o-dropdown-glass-border | 1px solid var(--o-border-glass) | Sets the popup border shorthand of a glass dropdown or select. |
--o-dropdown-glass-backdrop-filter | var(--o-backdrop-filter-glass) | Sets the filter applied to the content behind a glass popup. |
Override example
:root {
--o-dropdown-panel-min-width: 14rem;
--o-dropdown-panel-radius: var(--o-radius-round);
}Accessibility
The trigger is a native button named by the default slot. Its automatic Lucide icon is decorative and hidden from assistive technologies. The button exposes the popup role, expanded state and controlled panel. Opening focuses the first focusable element in the popup, or the popup itself when it contains none. Arrow Up, Arrow Down, Home and End move through menu items. Arrow Right opens a right-placed submenu and Arrow Left closes it; the directions reverse for a left-placed submenu. Escape closes the popup and restores trigger focus; moving focus or clicking outside closes it without stealing focus. Disabled dropdowns use the native disabled state.
Consumers must match popup content to popupRole: use menuitem descendants and menu keyboard behavior for menus, or provide appropriate dialog content for dialogs. Nested dropdowns can use a side placement for submenus.
Behavior and edge cases
Without open, the dropdown owns its open state. Providing open makes it controlled: interactions emit update:open, and the parent must update the value. The content slot's close action closes explicitly and restores focus by default. Bottom, top, left and right placements render the matching Lucide chevron after the default slot. Providing the optional trigger slot replaces both the default content and automatic icon, allowing complete trigger customization. Native attributes and listeners are forwarded to the trigger. The component renders deterministically during SSR and attaches positioning and dismissal behavior only on the client.
glass draws a translucent, blurred popup with a thin border through the --o-dropdown-glass-* properties, matching glass dialogs and cards; it follows the app's glass option unless set on the dropdown. Browsers without backdrop-filter show the translucent popup without blur.
<ODropdown>
<template #trigger>
<span class="custom-trigger">Open actions</span>
</template>
<template #content="{ close }">
<button role="menuitem" type="button" @click="close()">Archive</button>
</template>
</ODropdown>