{
  "generatedFrom": "menu.contract.json",
  "component": "Menu",
  "slug": "menu",
  "group": "Navigation",
  "tier": "leaf-lit",
  "summary": "A list of selectable options — a side nav, a settings list, an overflow menu — with icons, groups, submenus and danger actions.",
  "install": {
    "package": "@ahaslides-product/design",
    "registry": "https://npm.pkg.github.com",
    "scope": "@ahaslides-product",
    "auth": "Published to GitHub Packages — needs a GitHub token with read:packages. Configure the @ahaslides-product scope in .npmrc before installing.",
    "npmrc": "@ahaslides-product:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}   # a GitHub token with read:packages",
    "command": "npm i @ahaslides-product/design",
    "tokenLayer": "import '@ahaslides-product/design/tokens.css';",
    "import": "import '@ahaslides-product/design/aha-menu';",
    "element": "aha-menu",
    "registers": "aha-menu"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/menu/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/menu/menu.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/menu.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/menu.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-menu> is a standard custom element that renders on open — React/Vue are thin adapters over the same element. Paste into an .html file and it renders with no build step.",
  "frameworks": {
    "react": {
      "via": "web-component",
      "ref": "aha-menu"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-menu"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-menu",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "items",
      "type": "JSON tree",
      "default": "[]",
      "desc": "Rows. Leaf {key,label,icon?,disabled?,danger?}; submenu {key,label,icon?,children:[]}; section {type:'group',label,children:[]}; rule {type:'divider'}"
    },
    {
      "name": "value",
      "type": "string",
      "default": "—",
      "desc": "The selected item's key (brand-tinted row)"
    },
    {
      "name": "mode",
      "type": "'vertical' | 'inline' | 'horizontal'",
      "default": "vertical",
      "desc": "Layout: compact list · indented sidebar tree · top nav bar"
    },
    {
      "name": "select",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed event on selection, detail { key }"
    }
  ],
  "tokens": [
    "border",
    "bg-container",
    "bg-hover",
    "bg-accent",
    "text-default",
    "text-tertiary",
    "text-disabled",
    "color-primary",
    "color-error",
    "border-focus",
    "split",
    "radius-default"
  ],
  "spec": [
    {
      "label": "Container",
      "value": "white surface, 1px border #E3E3E3, radius 8, 6 padding, min-width 200"
    },
    {
      "label": "Row",
      "value": "height 36 · radius 8 · SemiBold 600 · 10px gap to a 16px leading icon"
    },
    {
      "label": "Group title",
      "value": "11px uppercase, text-tertiary #8A8A8A, non-interactive section header"
    },
    {
      "label": "Divider",
      "value": "1px split #F1F1F1 rule, role=separator"
    },
    {
      "label": "Submenu",
      "value": "caret rotates 90° + children expand inline (motion-mid), aria-expanded synced"
    },
    {
      "label": "Hover",
      "value": "bg-hover #F7F7F7 (motion-fast + ease-out, persistent node)"
    },
    {
      "label": "Selected",
      "value": "bg-accent #F9F5FF fill + color-primary #6A1EBB text"
    },
    {
      "label": "Danger",
      "value": "color-error #F5222D text, error-tinted hover"
    },
    {
      "label": "Disabled",
      "value": "text-disabled #B5B5B5, not-allowed"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Menu",
        "when": "a list of destinations or options — a side nav, a settings list, an overflow list"
      },
      {
        "what": "Tabs",
        "when": "switching horizontal views within one screen"
      },
      {
        "what": "Dropdown",
        "when": "the same list should sit inside a triggered overlay, not inline"
      }
    ],
    "note": "One selection at a time — reserve the brand tint for the active row. Keep labels short and in sentence case. Group related rows under a title; rule off a destructive action with a divider and mark it danger. For a triggered overlay, put a Menu inside a Dropdown rather than reinventing the list.",
    "dsv3": "COVERED — enumerated from the DS V3 Menu component-set taxonomy: Menu / Submenu/Inline, Menu Item / Inline / Group Title, / Inline / Navigation Item, / Horizontal menu, / Sidebar menu / Highest level, / Vertical / Popover Menu — i.e. the family carries modes (horizontal · inline/sidebar · vertical popover), group titles, submenus, navigation items, disabled + danger rows. Token layer: white surface, border #E3E3E3, bg-accent selected fill, color-primary label, color-error danger, text-tertiary group title, radius-default 8. Motion bound to the shared motion tokens on persistent nodes."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "settings",
    "admin"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-menu> is the SAME shared custom element React and Vue consume — here in its\n     native form. Theming comes only from the --aha-* tokens in tokens.css. -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css\">\n<script type=\"module\">\n  import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/icons.js';      // registers <aha-icon> for leading + caret glyphs\n  import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-menu.js';   // registers <aha-menu>\n</script>\n\n<!-- mode=\"vertical\" (default) | \"inline\" (sidebar tree) | \"horizontal\" (top nav bar) -->\n<aha-menu id=\"nav\" value=\"polls\"\n  items='[\n    {\"type\":\"group\",\"label\":\"Workspace\",\"children\":[\n      {\"key\":\"polls\",\"label\":\"Polls\",\"icon\":\"system-chart-bar\"},\n      {\"key\":\"quizzes\",\"label\":\"Quizzes\",\"icon\":\"system-question\"}\n    ]},\n    {\"key\":\"library\",\"label\":\"Content library\",\"icon\":\"system-folder\",\"children\":[\n      {\"key\":\"templates\",\"label\":\"Templates\"},\n      {\"key\":\"archived\",\"label\":\"Archived\",\"disabled\":true}\n    ]},\n    {\"type\":\"divider\"},\n    {\"key\":\"delete\",\"label\":\"Delete workspace\",\"icon\":\"system-trash\",\"danger\":true}\n  ]'>\n</aha-menu>\n\n<script>\n  // Selection animates on a persistent row; each pick emits a composed `select` event.\n  document.getElementById('nav').addEventListener('select', (e) => console.log('selected', e.detail.key));\n</script>\n",
    "react": "import '@ahaslides-product/design/icons';       // registers <aha-icon> for leading + caret glyphs\nimport '@ahaslides-product/design/aha-menu';    // registers <aha-menu>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper for the composed `select` event; React 19 can bind onSelect directly.\nfunction AhaMenu({ items, value, mode, onSelect }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    const h = (e) => onSelect?.(e.detail.key);\n    el.addEventListener('select', h);\n    return () => el.removeEventListener('select', h);\n  });\n  return <aha-menu ref={ref} value={value} mode={mode} items={JSON.stringify(items)} />;\n}\n\n// usage — icons, a titled group, a submenu, a danger action; mode: vertical | inline | horizontal\n<AhaMenu\n  value={view}\n  mode=\"vertical\"\n  items={[\n    { type: 'group', label: 'Workspace', children: [\n      { key: 'polls', label: 'Polls', icon: 'system-chart-bar' },\n      { key: 'quizzes', label: 'Quizzes', icon: 'system-question' },\n    ] },\n    { key: 'library', label: 'Content library', icon: 'system-folder', children: [\n      { key: 'templates', label: 'Templates' },\n      { key: 'archived', label: 'Archived', disabled: true },\n    ] },\n    { type: 'divider' },\n    { key: 'delete', label: 'Delete workspace', icon: 'system-trash', danger: true },\n  ]}\n  onSelect={setView}\n/>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/icons';       // registers <aha-icon> for leading + caret glyphs\nimport '@ahaslides-product/design/aha-menu';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue — icons, a titled group, a submenu, a danger action; mode: vertical | inline | horizontal\n<script setup>\nimport { ref, computed } from 'vue';\nconst view = ref('polls');\nconst items = computed(() => JSON.stringify([\n  { type: 'group', label: 'Workspace', children: [\n    { key: 'polls', label: 'Polls', icon: 'system-chart-bar' },\n    { key: 'quizzes', label: 'Quizzes', icon: 'system-question' },\n  ] },\n  { key: 'library', label: 'Content library', icon: 'system-folder', children: [\n    { key: 'templates', label: 'Templates' },\n    { key: 'archived', label: 'Archived', disabled: true },\n  ] },\n  { type: 'divider' },\n  { key: 'delete', label: 'Delete workspace', icon: 'system-trash', danger: true },\n]));\nfunction onSelect(e) { view.value = e.detail.key; }\n</script>\n\n<template>\n  <aha-menu :value=\"view\" mode=\"vertical\" :items=\"items\" @select=\"onSelect\" />\n</template>\n"
  }
}
