{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "context-menu",
  "name": "Context menu",
  "category": "Overlays",
  "description": "A menu that opens at the pointer on right-click, or from the keyboard with Shift+F10.",
  "tags": [
    "context menu",
    "right click",
    "right-click menu",
    "secondary menu",
    "actions menu"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "right click menu",
      "context menu"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/context-menu",
  "markdownUrl": "https://mian.ro/ai/components/context-menu.md",
  "classes": [
    {
      "name": "[data-mn-context=\"menu-id\"]",
      "description": "The area that opens the menu."
    },
    {
      "name": "mn-menu[popover]",
      "description": "The menu: the same markup as the dropdown menu."
    },
    {
      "name": "mn-context-area-dashed",
      "description": "A dashed drop-zone look for demos and canvases."
    }
  ],
  "accessibility": [
    "The area is focusable and announces aria-haspopup=\"menu\". Shift+F10 or the Menu key opens it at the area.",
    "Menu keyboard support (arrows, Home/End, typeahead, Escape) comes from the dropdown module; focus returns to the area on close.",
    "Right-click is not discoverable: offer the same actions elsewhere too."
  ],
  "examples": [
    {
      "title": "Right-click area",
      "description": null,
      "html": "<div class=\"mn-context-area mn-context-area-dashed\" data-mn-context=\"ctx-1\" aria-label=\"Canvas. Right-click or press Shift+F10 for actions\">Right-click here (or focus and press Shift+F10)</div>\n<div class=\"mn-menu\" id=\"ctx-1\" popover>\n  <button class=\"mn-menu-item\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"9\" y=\"9\" width=\"12\" height=\"12\" rx=\"2\"/><path d=\"M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1\"/></svg> Copy</button>\n  <button class=\"mn-menu-item\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M4 20h4L19 9l-4-4L4 16v4Z\"/><path d=\"m14 6 4 4\"/></svg> Rename</button>\n  <button class=\"mn-menu-item\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 4v12M7 11l5 5 5-5\"/><path d=\"M4 16v3a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-3\"/></svg> Download</button>\n  <div class=\"mn-menu-sep\" role=\"separator\"></div>\n  <button class=\"mn-menu-item mn-danger\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14\"/></svg> Delete</button>\n</div>"
    }
  ],
  "css": ".mn-context-area { position: relative; }\n.mn-context-area:focus-visible { outline: none; box-shadow: var(--mn-focus); }\n.mn-context-area-dashed { display: grid; place-items: center; min-height: 9rem; padding: 1.5rem; border: 1.5px dashed var(--mn-input); border-radius: var(--mn-radius-box); color: var(--mn-muted); font-size: var(--mn-text-sm); text-align: center; user-select: none; }",
  "js": "/* <div data-mn-context=\"menu-id\">…</div> + <div class=\"mn-menu\" id=\"menu-id\" popover>\n   Opens the menu at the pointer on right-click (or long-press where the\n   browser fires contextmenu), and from the keyboard with Shift+F10 or the\n   Menu key. Keyboard navigation comes from the dropdown module. */\nregister('context-menu', (root) => {\n  each(root, '[data-mn-context]', 'Ctx', (area) => {\n    const menu = document.getElementById(area.dataset.mnContext);\n    if (!menu || !menu.hasAttribute('popover')) return;\n    if (!area.hasAttribute('tabindex')) area.tabIndex = 0;\n    area.setAttribute('aria-haspopup', 'menu');\n    const open = (x, y) => {\n      if (menu.matches(':popover-open')) menu.hidePopover();\n      menu.showPopover();\n      const r = menu.getBoundingClientRect();\n      const vw = document.documentElement.clientWidth, vh = window.innerHeight;\n      menu.style.left = Math.round(Math.max(8, Math.min(x, vw - r.width - 8))) + 'px';\n      menu.style.top = Math.round(Math.max(8, Math.min(y, vh - r.height - 8))) + 'px';\n      const back = (ev) => { if (ev.newState === 'closed') { menu.removeEventListener('toggle', back); if (!menu.contains(document.activeElement) || document.activeElement === document.body) area.focus({ preventScroll: true }); } };\n      menu.addEventListener('toggle', back);\n    };\n    area.addEventListener('contextmenu', (e) => { e.preventDefault(); open(e.clientX, e.clientY); });\n    area.addEventListener('keydown', (e) => {\n      if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {\n        e.preventDefault();\n        const r = area.getBoundingClientRect();\n        open(r.left + Math.min(24, r.width / 2), r.top + Math.min(24, r.height / 2));\n      }\n    });\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/context-menu.json"
  }
}
