ContextMenu
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.
Show codeHide 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"; arole="menu"popover placed at the cursor, in the top layer (no ancestor can clip it). Fill it withDropdownItems (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:
Show codeHide 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 itemsrole="menuitem"(whatDropdownItemrenders). - 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.