Right-click menu on a region. The default slot is the region; a right-click inside it opens the rows at the pointer, clamped to the viewport, and focus lands on the first row. A row with items opens a flyout beside it. Set global to answer right-clicks anywhere on the page.
Right-click anywhere in the region, or Tab to it and press Shift+F10. The menu opens at the pointer or at the focused element, and focus lands on its first enabled row.
Write open with x and y to pin the menu. x and y are offsets in px from the top left corner of the region, and the panel is clamped to stay inside the viewport.
04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Pinned by the open prop
<div style="min-height:230px">
<sui-context-menu id="cm-pinned" open x="130" y="44">
<div tabindex="0" role="group" aria-label="Transaction lines" style="width:330px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface);overflow:hidden">
<div style="display:flex;justify-content:space-between;gap:16px;padding:8px 12px;border-bottom:1px solid var(--hairline);font-size:12.5px;color:var(--ink-muted)"><span style="color:var(--ink)">04 Mar</span><span style="flex:1">Fairview Supply Co</span><span style="font-feature-settings:'tnum';color:var(--ink)">$1,240.00</span></div>
<div style="display:flex;justify-content:space-between;gap:16px;padding:8px 12px;border-bottom:1px solid var(--hairline);font-size:12.5px;color:var(--ink-muted)"><span style="color:var(--ink)">06 Mar</span><span style="flex:1">Riverbend Dental Group</span><span style="font-feature-settings:'tnum';color:var(--ink)">$680.50</span></div>
<div style="display:flex;justify-content:space-between;gap:16px;padding:8px 12px;border-bottom:1px solid var(--hairline);font-size:12.5px;color:var(--ink-muted)"><span style="color:var(--ink)">09 Mar</span><span style="flex:1">City Power and Water</span><span style="font-feature-settings:'tnum';color:var(--ink)">$312.18</span></div>
<div style="padding:8px 12px;font-size:11.5px;color:var(--ink-faint)">Pinned by the open prop</div>
</div>
</sui-context-menu>
</div>
A row with its own items opens a flyout beside it, to any depth. It draws a chevron, reads aria-haspopup="menu" with aria-expanded, and opens its children instead of picking.
A row with href is a real link: suiPick fires and the menu closes on the click, then the browser follows. Open in a new tab and copy link work as on any link.
With global set, the element answers right-clicks anywhere on the document instead of inside a region, and x and y become viewport offsets. It is off here until you turn it on, because it would otherwise take over the right-click for this whole page.
Answer the whole pageOff. The browser keeps its own menu.
<div style="min-height:220px;display:flex;gap:16px;align-items:flex-start">
<sui-context-menu id="cm-global"></sui-context-menu>
<sui-button id="cm-global-btn" variant="outline">Answer the whole page</sui-button>
<span id="cm-global-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Off. The browser keeps its own menu.</span>
</div>
// setup(root): properties and events
setup(root) {
const el = root.querySelector('#cm-global');
const btn = root.querySelector('#cm-global-btn');
const log = root.querySelector('#cm-global-log');
el.items = lineActions();
btn.addEventListener('click', () => {
el.global = !el.global;
btn.textContent = el.global ? 'Hand the page back' : 'Answer the whole page';
log.textContent = el.global
? 'On. Right-click anywhere on this page.'
: 'Off. The browser keeps its own menu.';
});
el.addEventListener('suiOpen', (e) => {
log.textContent = `Opened at ${e.detail.x}, ${e.detail.y} in the viewport.`;
});
}
Events
suiOpen carries the point the menu opened at, and a pick emits suiPick and then runs the row command. suiClose fires before the menu closes with the reason (pick, outside or escape) and is cancelable, so preventDefault keeps it open.
Opens the menu from the keyboard, at the element that has focus. Give the region a focusable element, or a tabindex, so there is somewhere to press it.
ArrowDownArrowUp
Moves to the next or previous row of the level that has focus, wrapping. Separators, section eyebrows and disabled rows are skipped.
ArrowRight
Opens the focused row's submenu and lands on its first row.
ArrowLeft
Closes a submenu and returns focus to the row that opened it.
HomeEnd
Moves to the first or last enabled row of that level.
EnterSpace
Picks the focused row and closes the menu, or opens its submenu when it has one. On a row with href the browser then follows the link. On a close, focus returns to the element that had it when the menu opened.
Escape
Closes the menu and every submenu inside it, from anywhere on the page while it is open, and returns focus to the element that had it when the menu opened.
Tab
Closes the menu, hands focus back to the element that opened it and moves on from there. When nothing had focus, focus moves to the control after the region.
Any letter
Moves to the next row whose label starts with that letter, in the level that has focus.
API
<sui-context-menu>
Properties
Property
Attribute
Type
Default
Description
global
global
boolean
false
Answers right-clicks anywhere on the page instead of inside the slotted region, so one element serves a whole screen. The host takes no layout space, so leave the slot empty. When two elements set it, the one mounted last answers; a region menu that handles the click first wins over both.
items
property only
SuiMenuItem[]
[]
Rows (shared SuiMenuItem shape). A row with href is a real link: Enter follows it, and suiPick fires and the menu closes on the click first. A row with its own items opens a flyout beside it, to any depth. Property, not attribute.
open
open
boolean
false
Shows the menu. Write it with x and y to pin the menu open. Mutable: the element opens itself on right-click and closes on pick, outside pointer, Escape and Tab. An open menu paints in the browser's top layer, so a transformed, contained or clipping ancestor cannot move it or cut it off.
width
width
number
220
Width of the menu and of every flyout in px, the same prop sui-menu and sui-tiered-menu take. Widen it for long labels or long shortcuts, which otherwise wrap.
x
x
number
0
Horizontal offset of the menu in px, from the region's left edge or from the viewport's in global mode. The element sets it on right-click. Mutable.
y
y
number
0
Vertical offset of the menu in px, from the region's top edge or from the viewport's in global mode. The element sets it on right-click. Mutable.
Fires before the menu closes itself; detail is 'pick' | 'outside' | 'escape' ('outside' covers a pointer outside the menu, a press in the region, and Tab, which hands focus back to what had it when the menu opened and carries on from there). Cancelable: preventDefault() keeps it open. It does not bubble.
suiOpen
SuiContextMenuOpenDetail
onSuiOpen
@sui-open
Fires when a right-click opens the menu; detail is where it opened, the SuiContextMenuOpenDetail offset. It does not bubble.
suiPick
SuiMenuItem
onSuiPick
@sui-pick
Fires when a row is picked; detail is the item. item.command() also runs, then the menu closes.
Slots
Slot
Description
(default)
The region that responds to right-click. Leave it empty in global mode.
Types
SuiCloseReasontypeWhy a dismissible surface is closing. Detail of the cancelable suiClose. A consumer expresses "a stray backdrop click must not close this" by calling preventDefault() on the reason it does not accept, which is why none of these needs a prop of its own.
No members.
SuiContextMenuOpenDetailinterfaceWhere the menu opened, in px from the region's top-left corner (from the viewport's corner in global mode). Detail of suiOpen.
x
number
y
number
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.