Skip to content

Context Menu

Props ​

NameTypeDefaultDescription
itemsContextMenuOption[]-The data-driven menu content. An entry is an action row, a divider { type: 'divider' }, or a section { type: 'section', title, items }; a row that carries items becomes a submenu. Omit it and use the content slot.
menuWidthnumber | stringundefinedWidth of the menu panel. Numbers are interpreted as pixels. Without it the panel is at least 160px wide and sizes to its rows; either way it never overflows the viewport.
size'sm' | 'md' | 'lg''md'Row density. It changes the rows' block padding only; type, radius, and panel styling stay the same in every size.
labelstring'Context menu'Accessible name of the menu, announced through the panel's aria-label when it opens.
isDisabledbooleanfalseLeaves the right-click to the browser and never opens the menu; an open menu is closed. The native context menu still appears.
triggerAs'div' | 'span''div'Whether the trigger renders as a block div or an inline span for a word inside running prose.
v-model:isOpenboolean-Controlled open state. While bound the menu renders exactly this value; leave it unbound to let the menu own its state, opened by a right-click, the context-menu key, or a long press.

Slots ​

NameDescription
defaultThe trigger area: right-clicking this content opens the menu.
contentThe menu content in compound mode, composed from ContextMenuItem, ContextMenuDivider, ContextMenuGroup, and ContextMenuSubMenu. Ignored when items is given.

Events and template ref ​

NameSignatureDescription
openChange(isOpen: boolean) => voidFired when the menu opens or closes, before focus moves.
select(item: ContextMenuItemData, event: MouseEvent) => voidFired when a row of the items data API is activated, with the row and the click that activated it. Rows inside a submenu bubble up to the same event.
update:isOpen(isOpen: boolean) => voidListened to by v-model:isOpen; it rarely needs handling by hand.
elementHTMLElement | nullThe trigger element, exposed through a template ref.
open()() => voidOpens the menu at the trigger's top-left corner, for a caller with no pointer event to hand.
close()() => voidCloses the menu and returns focus to wherever it was before the menu opened.
focus() / blur()() => voidFocuses or blurs the trigger element.

Sub-components ​

Compound mode and data mode render the same row components, so their styling, keyboard behavior, and theme variables cannot drift apart.

ComponentPropsDescription
ContextMenuItemicon, label, description, endContent, href, target, rel, isDisabled, hasCloseOnSelect, variantOne action row, rendered as role="menuitem". With href the root becomes a real <a>, so a modified click and a middle click keep the browser's meaning. Slots: label, icon, description, endContent; emits click when activated.
ContextMenuDividervariantA rule between groups of rows, rendered as role="separator" so it is never a stop in the arrow order.
ContextMenuGrouptitleA titled group, rendered as role="group" named by its heading. Slot: default.
ContextMenuSubMenuicon, label, description, isDisabled, itemsA row that reveals a nested menu, carrying aria-haspopup="menu" and aria-expanded. Use items in data mode or the default slot in compound mode; emits openChange.

Theming ​

css
.my-context-menu {
  --astryx-context-menu-background: #ffffff;
  --astryx-context-menu-item-focus-background: #0536590c;
}
  • --astryx-context-menu-background: panel background.
  • --astryx-context-menu-foreground: panel text color.
  • --astryx-context-menu-shadow: panel shadow.
  • --astryx-context-menu-item-foreground: row text color.
  • --astryx-context-menu-item-focus-background: background of the highlighted row, for both hover and keyboard focus.
  • --astryx-context-menu-item-pressed-background: background of a row while a mouse holds it down, painted only where hover exists.
  • --astryx-context-menu-item-description-foreground: row description color.
  • --astryx-context-menu-item-error-foreground: label and icon color of a variant="destructive" row.
  • --astryx-context-menu-section-foreground: section heading color.
  • --astryx-context-menu-block-cap: maximum panel height, resolved against the viewport; the default takes the smaller of 300px and the viewport height.
  • --astryx-spacing-1, --astryx-radius-container, --astryx-text-label-size: padding, radius, and type reuse the global tokens.

The neutral theme adapts to light and dark mode on its own.

Native attributes ​

The root element is the trigger: a <div>, or a <span> with triggerAs="span".

The following go to the trigger element:

  • HTML attributes such as id or data-*.
  • aria-* attributes.

class and style, on the other hand, go to the menu panel: the panel is the painted, themeable surface (the astryx-context-menu class), which mirrors how upstream hands className and style to the menu.

The trigger itself carries no role, aria-haspopup, or aria-expanded: it is not a button, the right-click is a system-level gesture, and the menu is named by its own label only while it is open. The menu panel is teleported to document.body, so it inherits nothing from the trigger; the panel carries its own data-size, data-placement, data-overflow, data-width, and data-astryx-menu-press for styling and tests.