Vertical action menu: sections, icons, shortcuts, badges, danger and disabled rows. It paints rows only, so the surface it sits on is the one the reader sees: a sui-popover for a menu opened from a button, or variant="card" for a menu at rest on a page. Nested rows are the job of sui-tiered-menu.
The host aria-label names the role="menu". Each row is a role="menuitem"; a disabled row reads aria-disabled. variant="card" is the resting frame for a menu that stands on a page; the default paints rows only.
A row with href is a real link: middle-click and copy link work, Enter follows it, and suiPick and command still run on the click first. target sends it to a new tab. current marks the page the reader is on, bold and aria-current="page", the way a site menu does.
width sets a card menu in px. Widen it for long destination names, narrow it for a compact row menu. A plain menu fills the box it sits in instead, so a popover sets the width once.
// setup(root): properties and events
setup(root) {
root.querySelector('#menu-narrow').items = [
{ label: 'Edit', icon: 'fa-regular fa-pen-to-square' },
{ label: 'Copy', icon: 'fa-regular fa-copy' },
];
root.querySelector('#menu-wide').items = [
{ label: 'Post to the general ledger', icon: 'fa-solid fa-book' },
{ label: 'Move to a later accounting period', icon: 'fa-regular fa-calendar' },
];
}
In a popover
The menu has no popup mode: a sui-popover with flush and menu gives it a trigger and is the one card around the rows. The trigger announces a menu, opening puts focus on the first row so the arrows work at once, and a suiPick listener closes it.
The menu is one tab stop: Tab lands on the first enabled row and leaves on the next press.
ArrowDownArrowUp
Moves to the next or previous row, wrapping. Separators, section eyebrows and disabled rows are skipped.
HomeEnd
Moves to the first or last enabled row.
EnterSpace
Picks the focused row: emits suiPick and runs its command. On a row with href the browser then follows the link.
Any letter
Moves to the next row whose label starts with that letter.
API
<sui-menu>
Properties
Property
Attribute
Type
Default
Description
items
property only
SuiMenuItem[]
[]
Rows: { label, icon, shortcut, badge, danger, disabled, separator, section, command, href, target, current }. A row with href is a real link with the same styling and keyboard reach: Enter follows it, and suiPick and command still run on the click. A row with current is the page the reader is on: bold, and aria-current="page". A row's nested items are not opened here and the row says nothing about them: use sui-tiered-menu for a tree. Property, not attribute. Put an aria-label on the host to name the menu; a row menu beside a table row is otherwise announced with no context.
variant
variant
"card" | "plain"
'plain'
Surface the menu paints. plain is the default: rows only, no border and no shadow, because a menu is a list and whatever floats it paints the card. A sui-popover with flush does that for a menu opened from a button; sui-context-menu and sui-split-button do it for their own menus. card adds a resting border and rounded corners for a menu that stands on a page by itself, with no shadow, since nothing at rest casts one. Anything else falls back to plain.
width
width
number
230
Width of a card menu in px. A plain menu takes the width of the box it sits in, a popover panel or a card body, and ignores this.
Events
Event
Detail
React
Vue
Description
suiPick
SuiMenuItem
onSuiPick
@sui-pick
Fires when an enabled row is picked; detail is the item. item.command() also runs. On a link row both happen on the click, before the browser follows the href.
Methods
Method
Signature
Description
setFocus()
setFocus() => Promise<void>
Moves keyboard focus to the menu's one tab stop: the highlighted row, else the first enabled one.
Slots
Slot
Description
header
Optional header above the rows (e.g. the record id and its owner).
Types
SuiMenuIteminterface
label?
string
icon?
string
shortcut?
string
badge?
string | number
danger?
boolean
disabled?
boolean
separator?
boolean
section?
string
Section heading row (uppercase eyebrow).
items?
SuiMenuItem[]
Nested items (TieredMenu, Menubar dropdowns).
command?
() => void
href?
string
Renders the row as a real link, so middle-click, copy link and open in a new tab work and Enter follows it. The row keeps its styling, its place in the arrow-key order and its pick: command runs and suiPick fires on the click, then the browser navigates. Ignored on a disabled row and on a row with nested items; those stay buttons.
target?
string
Link target for a row with href, e.g. _blank. A _blank row gets rel="noopener".
current?
boolean
The row is the page the reader is on: it carries aria-current="page" and the bold face sui-side-nav gives its current item. For a navigation menu, the site pages behind a phone menu button; an action menu has no current row.
checked?
boolean
Makes the row one choice of a set, for a mode or an order picked from a menu: the chosen row draws a check and every row keeps room for one, and the rows read as menuitemradio with aria-checked. Leave it unset on an action row.
Theming
Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.