Skip to content
ShineUI
Install

ContextMenu

Right-click, act.

<sui-context-menu>Menus and navigationStable

About

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.

Usage

<sui-context-menu> <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)">Right-click a line</div> </div> </sui-context-menu> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-context-menu"); el.items = [ { label: "View transaction", icon: "fa-regular fa-eye" }, { label: "Match to bank feed", icon: "fa-solid fa-link" }, { label: "Split line", icon: "fa-solid fa-scissors" }, { separator: true }, { label: "Exclude", icon: "fa-regular fa-circle-xmark", danger: true }, ]; /* SuiMenuItem[] */ el.addEventListener("suiPick", (e) => console.log(e.detail)); </script>
import { SuiContextMenu } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const items = [ { label: "View transaction", icon: "fa-regular fa-eye" }, { label: "Match to bank feed", icon: "fa-solid fa-link" }, { label: "Split line", icon: "fa-solid fa-scissors" }, { separator: true }, { label: "Exclude", icon: "fa-regular fa-circle-xmark", danger: true }, ]; /* SuiMenuItem[] */ return ( <SuiContextMenu items={items} onSuiPick={(e) => console.log(e.detail)}> <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)">Right-click a line</div> </div> </SuiContextMenu> ); }
<script setup> import { SuiContextMenu } from "@shineui/vue"; import "@shineui/core/shineui.css"; const items = [ { label: "View transaction", icon: "fa-regular fa-eye" }, { label: "Match to bank feed", icon: "fa-solid fa-link" }, { label: "Split line", icon: "fa-solid fa-scissors" }, { separator: true }, { label: "Exclude", icon: "fa-regular fa-circle-xmark", danger: true }, ]; // SuiMenuItem[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiContextMenu :items="items" @sui-pick="onChange"><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)">Right-click a line</div> </div></SuiContextMenu> </template>
import { Component } from "@angular/core"; import { SuiContextMenu } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiContextMenu], template: `<sui-context-menu [(open)]="open" [items]="items" (suiPick)="onChange($event)"><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)">Right-click a line</div> </div></sui-context-menu>`, }) export class Example { open = false; items = [ { label: "View transaction", icon: "fa-regular fa-eye" }, { label: "Match to bank feed", icon: "fa-solid fa-link" }, { label: "Split line", icon: "fa-solid fa-scissors" }, { separator: true }, { label: "Exclude", icon: "fa-regular fa-circle-xmark", danger: true }, ]; // SuiMenuItem[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

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.

04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Right-click a line
<sui-context-menu> <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)">Right-click a line</div> </div> </sui-context-menu>
// setup(root): properties and events setup(root) { root.querySelector('sui-context-menu').items = lineActions(); }

Pinned open

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>
// setup(root): properties and events setup(root) { root.querySelector('#cm-pinned').items = lineActions(); }

Danger and disabled rows

A danger row takes the danger tokens and still picks normally. A disabled row is inert: the pointer ignores it and the keyboard steps over it.

04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Right-click a line
<sui-context-menu id="cm-states"> <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)">Right-click a line</div> </div> </sui-context-menu>
// setup(root): properties and events setup(root) { root.querySelector('#cm-states').items = [ { section: 'This line' }, { label: 'View transaction', icon: 'fa-regular fa-eye' }, { label: 'Edit', icon: 'fa-regular fa-pen-to-square', shortcut: 'E' }, { label: 'Reconcile', icon: 'fa-solid fa-check-double', disabled: true }, { separator: true }, { label: 'Delete line', icon: 'fa-regular fa-trash-can', danger: true }, ]; }

Submenus

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.

04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Right-click a line, then open Move to
<div style="min-height:260px"> <sui-context-menu id="cm-sub"> <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)">Right-click a line, then open Move to</div> </div> </sui-context-menu> </div>
// setup(root): properties and events setup(root) { root.querySelector('#cm-sub').items = [ { label: 'View transaction', icon: 'fa-regular fa-eye' }, { label: 'Move to', icon: 'fa-solid fa-right-left', items: [ { section: 'Bank' }, { label: 'Operating 4402' }, { label: 'Payroll 4410' }, { separator: true }, { label: 'Older accounts', items: [{ label: 'Reserve 4390' }, { label: 'Escrow 4381', disabled: true }], }, ], }, { separator: true }, { label: 'Exclude', icon: 'fa-regular fa-circle-xmark', danger: true }, ]; }

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.

04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Right-click a line
<sui-context-menu id="cm-links"> <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)">Right-click a line</div> </div> </sui-context-menu>
// setup(root): properties and events setup(root) { root.querySelector('#cm-links').items = [ { label: 'Open transaction', icon: 'fa-regular fa-eye', href: '#/transactions/88' }, { label: 'Open payee', icon: 'fa-regular fa-user', href: '#/payees/fairview' }, { separator: true }, { label: 'Exclude', icon: 'fa-regular fa-circle-xmark', danger: true }, ]; }

Whole page

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 page Off. 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.

04 MarFairview Supply Co$1,240.00
06 MarRiverbend Dental Group$680.50
09 MarCity Power and Water$312.18
Right-click a line
Closed.
<div style="min-height:220px;display:flex;gap:16px;align-items:flex-start"> <sui-context-menu id="cm-events"> <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)">Right-click a line</div> </div> </sui-context-menu> <span id="cm-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Closed.</span> </div>
// setup(root): properties and events setup(root) { const log = root.querySelector('#cm-log'); const el = root.querySelector('#cm-events'); const items = lineActions(); items[0].command = () => { log.textContent += ' Its command ran.'; }; el.items = items; el.addEventListener('suiOpen', (e) => { log.textContent = `Opened at ${e.detail.x}, ${e.detail.y}.`; }); el.addEventListener('suiClose', (e) => { log.textContent = `Closed by ${e.detail}.`; }); el.addEventListener('suiPick', (e) => { log.textContent = `Picked ${e.detail.label}.`; }); }

Keyboard

KeyAction
Shift+F10 MenuOpens 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.
ArrowDown ArrowUpMoves to the next or previous row of the level that has focus, wrapping. Separators, section eyebrows and disabled rows are skipped.
ArrowRightOpens the focused row's submenu and lands on its first row.
ArrowLeftCloses a submenu and returns focus to the row that opened it.
Home EndMoves to the first or last enabled row of that level.
Enter SpacePicks 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.
EscapeCloses 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.
TabCloses 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 letterMoves to the next row whose label starts with that letter, in the level that has focus.

API

<sui-context-menu>

Properties

PropertyAttributeTypeDefaultDescription
globalglobalbooleanfalseAnswers 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.
itemsproperty onlySuiMenuItem[][]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.
openopenbooleanfalseShows 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.
widthwidthnumber220Width 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.
xxnumber0Horizontal 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.
yynumber0Vertical 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.

Events

EventDetailReactVueDescription
suiCloseSuiCloseReason"cancel" | "close-button" | "escape" | "outside" | "pick" | "scrim" | "trigger"onSuiClose@sui-closeFires 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.
suiOpenSuiContextMenuOpenDetailonSuiOpen@sui-openFires when a right-click opens the menu; detail is where it opened, the SuiContextMenuOpenDetail offset. It does not bubble.
suiPickSuiMenuItemonSuiPick@sui-pickFires when a row is picked; detail is the item. item.command() also runs, then the menu closes.

Slots

SlotDescription
(default)The region that responds to right-click. Leave it empty in global mode.

Types

SuiCloseReason typeWhy 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.
SuiContextMenuOpenDetail interfaceWhere the menu opened, in px from the region's top-left corner (from the viewport's corner in global mode). Detail of suiOpen.
xnumber
ynumber
SuiMenuItem interface
label?string
icon?string
shortcut?string
badge?string | number
danger?boolean
disabled?boolean
separator?boolean
section?stringSection heading row (uppercase eyebrow).
items?SuiMenuItem[]Nested items (TieredMenu, Menubar dropdowns).
command?() => void
href?stringRenders 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?stringLink target for a row with href, e.g. _blank. A _blank row gets rel="noopener".
current?booleanThe 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?booleanMakes 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.

See also