Menu
Anatomy
Dropdown menu for actions and navigation. Supports items, separators, labels, checkboxes, radio groups, submenus, and grouping.
maud_ui::primitives::menu
Props
Generated from public Rust fields. Defaults are evaluated from the implementation.
| Prop | Type | Default | Description |
|---|---|---|---|
trigger_label | String | No struct Default; supply explicitly | Text displayed on the trigger button |
id | String | No struct Default; supply explicitly | Unique identifier for the menu |
items | Vec<MenuEntry> | No struct Default; supply explicitly | Menu entries (items, separators, and labels) |
Examples
File menu
User menu
View menu — checkbox + radio + submenu
Usage and composition
Import
use maud_ui::primitives::menu::{self, Props, MenuItem, MenuEntry, RadioItem};
Example
use maud::html;
use maud_ui::primitives::menu;
html! {
(menu::render(menu::Props {
trigger_label: "Actions".into(),
id: "demo-menu".into(),
items: vec![
menu::MenuEntry::Item(menu::MenuItem {
label: "Edit".into(),
action: "edit".into(),
disabled: false,
destructive: false,
shortcut: Some("⌘E".into()),
}),
menu::MenuEntry::Separator,
menu::MenuEntry::Item(menu::MenuItem {
label: "Delete".into(),
action: "delete".into(),
disabled: false,
destructive: true,
shortcut: None,
}),
],
}))
}
MenuEntry Enum
The MenuEntry enum defines the structure of menu items. All variants are fully supported:
MenuEntry::Item(MenuItem)
A simple clickable menu item.
pub struct MenuItem {
pub label: String, // Item text
pub action: String, // Data attribute for event handling
pub disabled: bool, // Disabled state
pub destructive: bool, // Red danger styling (e.g., delete)
pub shortcut: Option<String>, // Keyboard shortcut display (e.g., "⌘Z")
}
MenuEntry::Separator
A visual divider between menu sections.
MenuEntry::Label(String)
A non-interactive label or heading for grouping related items.
MenuEntry::CheckboxItem
A menu item with toggle state (on/off).
MenuEntry::CheckboxItem {
label: String, // Item text
checked: bool, // Current state
disabled: bool, // Disabled state
shortcut: Option<String>, // Optional keyboard shortcut
}
MenuEntry::RadioGroup
A named group of mutually exclusive radio options.
MenuEntry::RadioGroup {
name: String, // Group identifier
items: Vec<RadioItem>, // Radio options
}
Where RadioItem is:
pub struct RadioItem {
pub label: String, // Option text
pub value: String, // Option value
pub checked: bool, // Currently selected
pub disabled: bool, // Disabled state
pub shortcut: Option<String>, // Optional keyboard shortcut
}
MenuEntry::Sub
A submenu trigger with nested menu entries.
MenuEntry::Sub {
trigger_label: String, // Submenu trigger text
items: Vec<MenuEntry>, // Nested menu entries (any MenuEntry variant)
}
Submenus can be nested arbitrarily deep.
MenuEntry::Group
A generic grouping wrapper (renders with role="group").
MenuEntry::Group {
items: Vec<MenuEntry>, // Grouped entries
}
Variants
| Variant | Use Case |
|---|---|
| Item | Primary menu action. |
| Item (destructive) | Dangerous action requiring confirmation (delete, remove, etc.). |
| Separator | Visual divider between logical sections. |
| Label | Section header or grouping label. |
| CheckboxItem | Toggle option (show/hide, enable/disable feature). |
| RadioGroup | Single-selection option group (e.g., theme, position). |
| Sub | Nested menu with additional options. |
| Group | Container for related menu entries. |
Helpers
render_entry(entry: &MenuEntry) -> Markup
Renders a single menu entry. Used internally and can be used in custom menu construction. Public for composability.
Runtime lifecycle
In the Nova profile, closing popups and directly closed submenus stay mounted until their own finite animations finish. Reopening cancels the pending exit. Removing the parent, destroying the runtime or normalizing browser history also cleans up descendant menus and restores any portalled nodes. Caller-authored finite popup animations participate in this lifecycle; infinite animations do not delay removal. The Standard profile retains its existing dismissal behavior. Submenus use their trigger label as the accessible menu name.
Related
Context Menu, Menubar, Button.
Shadcn reference
https://ui.shadcn.com/docs/components/base/dropdown-menu
Accessibility
- Trigger button: Sets
aria-expanded="false"(true when open),aria-haspopup="menu", andaria-controlspointing to the menu content. - Menu roles: Menu items use
role="menuitem", checkboxes userole="menuitemcheckbox"witharia-checked, radio buttons userole="menuitemradio"witharia-checked, and separators userole="separator". - Keyboard navigation: Tabindex is set to
-1for all menu items; keyboard navigation (arrow keys, Enter, Escape) is handled via JavaScript. - Shortcuts: Displayed as decorative text; not enforced by the component (handled by application).
- Screen readers: Labels are announced for all entry types; separators are marked as decorative.
The popup preserves caller-authored inline pointer-events values and priorities through opening, closing and restoration. Its default closing hit suppression comes from the closing-state CSS.