Context Menu — <mv-context-menu>
Context menu opened at the pointer (right-click), on touch long-press or from the keyboard (Shift+F10 / Menu key); reuses the Menu engine and styles, including the data-motion="glide" animation.
Category Overlays Type Web Component (<mv-context-menu>) Status stable Also installs menu , popover Keywords context-menu, right-click, long-press, menu, submenu
When to use
Items in a file list, canvas or table need actions on right-click or touch long-press Power users expect contextual actions at the pointer, with keyboard access through Shift+F10 or the Menu key
Avoid when
The actions are hidden only behind right-click with no visible trigger to discover them → use Menu instead The area is plain reading content where users expect the browser menu for copy and search
Get Context Menu. Included in every plan: the Pro pack ships all 226 components with the CLI and the MCP server used below.
See plans
Install
node scripts/add.mjs context-menu --out ./src/marvelous
AI agent with the Marvelous UI MCP server: install_components({ slugs: ["context-menu"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dismiss.js, core/dom.js, core/element.js, core/focus.js, core/motion.js, core/position.js, components/context-menu/context-menu.js, components/context-menu/context-menu.css, components/menu/menu.js, components/menu/menu.css, components/popover/popover.js, components/popover/popover.css.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<div id="ctx-demo" style="display:grid;gap:1rem;width:min(26rem,100%)">
<mv-context-menu>
<div class="mv-context-menu-area" aria-label="Layer “Hero banner”" style="min-height:11rem">
<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="opacity:.6"><path d="m12 3 9 5-9 5-9-5z"/><path d="m3 13 9 5 9-5"/></svg>
<strong style="color:var(--mv-fg);font-weight:500">Right-click the layer</strong>
<span style="font-size:.75rem">Long-press on mobile · Shift + F10 on keyboard</span>
</div>
<div data-content style="--mv-menu-width:15rem">
<button role="menuitem" value="copy">Copy <span class="mv-menu-shortcut">⌘C</span></button>
<button role="menuitem" value="paste" disabled>Paste <span class="mv-menu-shortcut">⌘V</span></button>
<button role="menuitem" value="duplicate">Duplicate <span class="mv-menu-shortcut">⌘D</span></button>
<div data-submenu>
<button role="menuitem">Arrange</button>
<div data-content style="--mv-menu-width:13rem">
<button role="menuitem" value="bring-to-front">Bring to front <span class="mv-menu-shortcut">⇧⌘]</span></button>
<button role="menuitem" value="bring-forward">Bring forward <span class="mv-menu-shortcut">⌘]</span></button>
<button role="menuitem" value="send-backward">Send backward <span class="mv-menu-shortcut">⌘[</span></button>
<button role="menuitem" value="send-to-back">Send to back <span class="mv-menu-shortcut">⇧⌘[</span></button>
</div>
</div>
<hr>
<button role="menuitemcheckbox" value="lock">Lock <span class="mv-menu-shortcut">⇧⌘L</span></button>
<button role="menuitemcheckbox" value="hide">Hide <span class="mv-menu-shortcut">⇧⌘H</span></button>
<hr>
<div role="group" data-name="blend">
<div class="mv-menu-label" data-inset>Blend mode</div>
<button role="menuitemradio" value="normal" aria-checked="true">Normal</button>
<button role="menuitemradio" value="multiply">Multiply</button>
<button role="menuitemradio" value="overlay">Overlay</button>
</div>
<hr>
<button role="menuitem" value="delete" data-variant="destructive">Delete <span class="mv-menu-shortcut">⌫</span></button>
</div>
</mv-context-menu>
<mv-context-menu data-motion="glide">
<div class="mv-context-menu-area" aria-label="File “q3-report.pdf”" style="min-height:6rem">
<strong style="color:var(--mv-fg);font-weight:500">data-motion="glide"</strong>
<span style="font-size:.75rem">The menu blooms from the pointer, the highlight glides</span>
</div>
<div data-content style="--mv-menu-width:13rem">
<button role="menuitem" value="open">Open <span class="mv-menu-shortcut">↵</span></button>
<button role="menuitem" value="quick-look">Quick Look <span class="mv-menu-shortcut">␣</span></button>
<div data-submenu>
<button role="menuitem">Open with</button>
<div data-content>
<button role="menuitem" value="app-reader">PDF reader</button>
<button role="menuitem" value="app-browser">Browser</button>
</div>
</div>
<hr>
<button role="menuitem" value="rename">Rename</button>
<button role="menuitem" value="delete" data-variant="destructive">Delete <span class="mv-menu-shortcut">⌫</span></button>
</div>
</mv-context-menu>
<p data-role="out" aria-live="polite" style="margin:0;text-align:center;font-size:.8125rem;color:var(--mv-fg-muted)">No action yet.</p>
</div>
<script type="module">
const root = document.getElementById("ctx-demo");
const out = root.querySelector('[data-role="out"]');
root.addEventListener("mv-select", (e) => {
const { label, checked, group } = e.detail;
out.textContent = group ? `Blend: ${label}` : checked === undefined ? `Action: ${label}` : `${label}: ${checked ? "on" : "off"}`;
});
</script>
API
Attributes
Name Type Description disabledboolean Lets the browser’s native menu through. data-motion"glide" Same option as mv-menu: the panel blooms from the click point, the highlight glides between items, submenus slide in.
Properties
Name Type Description isOpenboolean Read-only. menuMenuPanel Instance of the engine shared with mv-menu.
Methods
Name Description openAt(x, y, { focus })Opens at viewport coordinates; focus = "first" | "panel". hide({ focus })Closes; focus=true returns focus to the element that was active before opening (or to the area).
Events
Name Description mv-selectCancelable. detail: { value, label, item, checked?, group? } (same as mv-menu). mv-opendetail: { x, y }. mv-closeAfter closing.
Content structure
Name Description first childTarget area; made focusable (tabindex=0) if it isn’t. Its aria-label names the menu. [data-content]Panel: same markup as mv-menu (menuitem, checkbox, radio, [data-submenu], <hr>, .mv-menu-label, .mv-menu-shortcut).
CSS classes
Name Description mv-context-menu-areaReady-made dashed area (visual feedback during long-press).
CSS variables
Name Description --mv-menu-*All Menu variables apply (width, highlight, background, radius).
Accessibility
role=menu panel (APG “Menu” pattern) with full navigation: ↑↓ Home/End, typeahead, → / ← for submenus, Escape and Tab close. Keyboard opening via Shift+F10 or the Menu key from the focusable area (focus on the first item); focus then returns to the original element.