Dropdown menu component with smart positioning, click-outside detection, and full WAI-ARIA keyboard navigation.
Requires the PhiaDropdownMenu JavaScript hook registered in app.js.
The hook handles all interactivity: opening/closing the panel, focusing
the first menu item on open, and full keyboard navigation (Arrow keys,
Home, End, Escape, Tab).
Hook Registration
Copy the hook via mix phia.add dropdown_menu, then register it:
# assets/js/app.js
import PhiaDropdownMenu from "./hooks/dropdown_menu"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { PhiaDropdownMenu }
})Sub-components
| Function | Element | Purpose |
|---|---|---|
dropdown_menu/1 | div | Relative container — JS hook anchor |
dropdown_menu_trigger/1 | button | Toggle button with aria-haspopup/aria-expanded |
dropdown_menu_content/1 | div | Floating menu panel (role="menu") |
dropdown_menu_item/1 | div | Clickable item (role="menuitem") |
dropdown_menu_label/1 | div | Non-interactive section heading |
dropdown_menu_separator/1 | hr | Visual divider between sections |
dropdown_menu_group/1 | div | Logical grouping wrapper |
dropdown_menu_checkbox_item/1 | div | Toggle item (role="menuitemcheckbox") |
dropdown_menu_radio_group/1 | div | Container for mutually exclusive items |
dropdown_menu_radio_item/1 | div | Radio item (role="menuitemradio") |
dropdown_menu_shortcut/1 | span | Right-aligned keyboard shortcut hint |
Basic Example — Account Menu
<.dropdown_menu id="user-menu">
<.dropdown_menu_trigger>
My Account
</.dropdown_menu_trigger>
<.dropdown_menu_content>
<.dropdown_menu_label>My Account</.dropdown_menu_label>
<.dropdown_menu_separator />
<.dropdown_menu_group>
<.dropdown_menu_item phx-click="goto_profile">
Profile
<.dropdown_menu_shortcut>⌘P</.dropdown_menu_shortcut>
</.dropdown_menu_item>
<.dropdown_menu_item phx-click="goto_settings">
Settings
<.dropdown_menu_shortcut>⌘S</.dropdown_menu_shortcut>
</.dropdown_menu_item>
</.dropdown_menu_group>
<.dropdown_menu_separator />
<.dropdown_menu_item variant="destructive" phx-click="logout">
Log out
</.dropdown_menu_item>
</.dropdown_menu_content>
</.dropdown_menu>Checkbox Items Example — View Options
<.dropdown_menu id="view-menu">
<.dropdown_menu_trigger>View</.dropdown_menu_trigger>
<.dropdown_menu_content>
<.dropdown_menu_label>Appearance</.dropdown_menu_label>
<.dropdown_menu_separator />
<.dropdown_menu_checkbox_item
checked={@show_toolbar}
phx-click="toggle_toolbar">
Show Toolbar
</.dropdown_menu_checkbox_item>
<.dropdown_menu_checkbox_item
checked={@show_statusbar}
phx-click="toggle_statusbar">
Show Status Bar
</.dropdown_menu_checkbox_item>
</.dropdown_menu_content>
</.dropdown_menu>Radio Items Example — Theme Selector
<.dropdown_menu id="theme-menu">
<.dropdown_menu_trigger>Theme</.dropdown_menu_trigger>
<.dropdown_menu_content>
<.dropdown_menu_radio_group>
<.dropdown_menu_radio_item
:for={theme <- ~w(Light Dark System)}
checked={theme == @current_theme}
phx-click="set_theme"
phx-value-theme={theme}>
{theme}
</.dropdown_menu_radio_item>
</.dropdown_menu_radio_group>
</.dropdown_menu_content>
</.dropdown_menu>Keyboard Navigation
The PhiaDropdownMenu hook provides full WAI-ARIA keyboard support:
Enter/Space— opens the menu when trigger is focusedArrowDown/ArrowUp— moves focus between itemsHome/End— jumps to first / last itemEscape— closes the menu and returns focus to the triggerTab— closes the menu naturally (focus leaves the component)
Accessibility
- Trigger has
aria-haspopup="menu"andaria-expanded(toggled by hook) - Menu panel has
role="menu"andaria-orientation="vertical" - Each item has
role="menuitem"(ormenuitemcheckbox/menuitemradio) - Disabled items use
aria-disabled="true"andpointer-events-nonerather than the HTMLdisabledattribute, becausedisabledremoves items from the accessibility tree and breaks keyboard navigation
Summary
Functions
Renders the relative-positioned container that anchors the JS hook.
Renders a toggleable menu item with a checkmark indicator.
Renders the floating menu panel.
Wraps a logical group of menu items together.
Renders a clickable menu item with role="menuitem".
Renders a non-interactive section heading inside the menu.
Wraps a set of mutually exclusive dropdown_menu_radio_item/1 components.
Renders a radio-style menu item with a filled bullet indicator.
Renders a horizontal visual separator between groups of menu items.
Renders a right-aligned keyboard shortcut hint inside a menu item.
Wraps a submenu — a trigger item that reveals a nested menu panel.
Renders the submenu content panel positioned to the right of the trigger.
Renders an item that opens a submenu on hover or click.
Renders the trigger button that opens and closes the dropdown menu.