A styleable right-click menu — a native popover at the cursor over a target; closes on outside click, Escape, or scroll.

Last updated

Uses JavaScript

Opens a native popover at the cursor on right-click (top layer), moves focus to the first item, wires the arrow keys, closes on item activation and on scroll or resize. Outside click and Escape are the browser’s own light dismiss.

Without JS: The panel stays hidden and the target's normal browser right-click menu opens instead — nothing is broken or lost.

Right-click here

Show code
<ContextMenu class="w-full">
<div class="grid h-24 w-full place-items-center rounded-md border border-dashed border-neutral text-sm text-neutral-faded">
  Right-click here
</div>
<DropdownItem slot="menu">Cut</DropdownItem>
<DropdownItem slot="menu">Copy</DropdownItem>
<DropdownItem slot="menu">Paste</DropdownItem>
</ContextMenu>

Items accept indicator="check" | "checkbox" | "radio" like Dropdown.

Add

npx @ava-ui/apogee add context-menu

No Apogee dependencies. Pair with DropdownItem for menu entries.

Usage

---
import ContextMenu from '@/components/apogee/ContextMenu/ContextMenu.astro'
import DropdownItem from '@/components/apogee/Dropdown/DropdownItem.astro'
---
<ContextMenu>
  <div>Right-click target</div>
  <DropdownItem slot="menu">Copy</DropdownItem>
  <DropdownItem slot="menu">Paste</DropdownItem>
</ContextMenu>

Anatomy

  • Target — default slot; the element(s) that trigger the menu on right-click.
  • Menu panel — slot="menu"; a role="menu" popover placed at the cursor, in the top layer (no ancestor can clip it). Fill it with DropdownItems (role="menuitem").

API

@composable — exposed to Content Editors in AVA. Any other attribute passes through to the root <div>.

Prop Type Default Description
class string — Extra classes on the root wrapper, merged last.

States

  • Closed (default) — the popover is closed.
  • Open — right-click (or the keyboard context-menu key / Shift+F10) shows the panel at the cursor, clamped inside the viewport. Try it in the hero preview above.
  • No JS — a closed popover renders nothing; the browser’s native context menu opens instead.

Examples

With leading icons:

Right-click here

Show code
<ContextMenu>
<div class="grid h-24 w-full place-items-center rounded-md border border-dashed border-neutral text-sm text-neutral-faded">
  Right-click here
</div>
<DropdownItem slot="menu"><Scissors /> Cut</DropdownItem>
<DropdownItem slot="menu"><Copy /> Copy</DropdownItem>
<DropdownItem slot="menu"><ClipboardPaste /> Paste</DropdownItem>
</ContextMenu>

Events

The panel dispatches apogee:contextmenu-open (detail: { x, y }, the clamped position) and apogee:contextmenu-close, both bubbling, alongside the native toggle event.

Accessibility

Key Action
Context-menu key, Shift F10 Opens the menu and focuses the first item.
↓ ↑ Move between items; wraps.
Home End First / last item.
Enter Space Activate the focused item and close the menu.
Esc Closes the menu and returns focus.
  • The panel has role="menu"; give items role="menuitem" (what DropdownItem renders).
  • No JS — the panel never opens; the browser’s native context menu opens instead, so the actions remain reachable through it.

Tokens

Token Used for
--border-neutral Panel border
--background-elevation-overlay Panel background
--duration-fast / --ease-standard Panel open fade/scale animation

Do / Don’t

  • Do keep items short and task-focused (Cut, Copy, Paste, Delete).
  • Do offer the same actions somewhere else too (a toolbar, a keyboard shortcut) — right-click alone isn’t discoverable for every user.
  • Don’t nest anything but menu items (DropdownItem) in the panel.
  • Don’t treat it as the only way to reach an action — no-JS visitors fall back to the native menu, which won’t show your custom entries.

Content

  • Item labels are short verbs (Cut, Copy, Paste, Delete), sentence case, no ending punctuation — same rules as DropdownItem.