{
  "generatedFrom": "dropdown.contract.json",
  "component": "Dropdown",
  "slug": "dropdown",
  "group": "Navigation",
  "tier": "leaf-lit",
  "summary": "A trigger button that reveals a floating overlay of actions — with leading icons, dividers, disabled rows and a red danger action.",
  "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-dropdown';",
    "element": "aha-dropdown",
    "registers": "aha-dropdown"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/dropdown/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/dropdown/dropdown.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/dropdown.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/dropdown.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-dropdown> 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-dropdown"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-dropdown"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-dropdown",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "label",
      "type": "string",
      "default": "Menu",
      "desc": "The trigger button text"
    },
    {
      "name": "items",
      "type": "JSON list",
      "default": "[]",
      "desc": "Rows. Leaf {key,label,icon?,disabled?,danger?}; submenu-parent {key,label,icon?,children:[…]} → adjacent flyout; rule {type:'divider'}; section {type:'group',label,children:[]}"
    },
    {
      "name": "placement",
      "type": "'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight'",
      "default": "bottomLeft",
      "desc": "Where the overlay opens relative to the trigger"
    },
    {
      "name": "trigger",
      "type": "'click' | 'hover'",
      "default": "click",
      "desc": "How the overlay is revealed"
    },
    {
      "name": "select",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed event on choosing an item, detail { key }"
    }
  ],
  "tokens": [
    "border",
    "border-hover",
    "bg-container",
    "bg-elevated",
    "bg-hover",
    "bg-accent",
    "text-default",
    "text-tertiary",
    "text-disabled",
    "icon-muted",
    "color-error",
    "border-focus",
    "split",
    "radius-default"
  ],
  "spec": [
    {
      "label": "Trigger",
      "value": "height 40 · radius 8 · white bg, 1px border #E3E3E3, SemiBold 600"
    },
    {
      "label": "Trigger hover",
      "value": "border-hover #D3B4FF (motion-fast + ease-out)"
    },
    {
      "label": "Caret",
      "value": "<aha-icon name=\"system-caret-down\" size=16>, rotates 180° when open (motion-mid)"
    },
    {
      "label": "Panel",
      "value": "elevated white, radius 8, 6 padding, min-width 200, soft shadow"
    },
    {
      "label": "Placement",
      "value": "bottomLeft · bottomRight · topLeft · topRight — anchors the persistent panel"
    },
    {
      "label": "Trigger mode",
      "value": "click (default) or hover"
    },
    {
      "label": "Open motion",
      "value": "opacity + translateY on a persistent node (motion-mid + ease-out)"
    },
    {
      "label": "Item",
      "value": "height 36 · radius 8 · 10px gap to a 16px leading icon · hover bg-hover #F7F7F7"
    },
    {
      "label": "Danger item",
      "value": "color-error #F5222D text, error-tinted hover"
    },
    {
      "label": "Divider",
      "value": "1px split #F1F1F1 rule, role=separator"
    },
    {
      "label": "Group title",
      "value": "11px uppercase, text-tertiary #8A8A8A, non-interactive section header"
    },
    {
      "label": "Disabled item",
      "value": "text-disabled #B5B5B5, not-allowed"
    },
    {
      "label": "Submenu parent",
      "value": "item with children[]; aria-haspopup=menu, trailing <aha-icon name=\"system-caret-right\" size=14>; brand-tinted (bg-accent #F9F5FF, color-primary #6A1EBB) while its flyout is open"
    },
    {
      "label": "Flyout",
      "value": "second elevated panel adjacent to the parent (left:100%), opens on hover / ArrowRight / Enter; fades+scales on a persistent node (motion-mid + ease-out)"
    },
    {
      "label": "Edge-collision flip",
      "value": "flyout measures on open; if it overflows the viewport it flips to open on the left (right:100%)"
    },
    {
      "label": "Flyout keyboard",
      "value": "Up/Down within the flyout, ArrowLeft / Escape closes it back to the parent (focus returns to the parent row)"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Dropdown",
        "when": "a set of actions or options should sit behind a trigger to save space"
      },
      {
        "what": "Menu",
        "when": "the same list belongs inline, always visible — not behind a trigger"
      },
      {
        "what": "Select",
        "when": "the control picks a single value for a form field, not fires an action"
      }
    ],
    "note": "A dropdown holds actions or navigation, not form values — use Select for a field. Keep the item list short; the panel closes on outside-click and Escape. Give each action a leading icon, rule off a destructive action with a divider and mark it danger. Reach for a submenu-parent (children[]) only when a group of related actions would otherwise crowd the top level — one level of flyout is plenty; deeper nesting is a menu smell. The overlay reuses the shared Menu item vocabulary rather than reinventing the row styling.",
    "dsv3": "COVERED — enumerated from the DS V3 Dropdown-Menu + Components/Dropdown/Menu-Item + Dropdown/Menu-Item-Group component sets: the overlay is a Menu of items carrying leading icons, dividers, group titles, disabled + danger rows — the same item family as Menu — plus submenu-parents whose children[] open an adjacent flyout (the DS V3 Menu-Item nested/expandable variant, here as a floating flyout with a trailing system-caret-right and aria-haspopup). Reference contract measured keys (dropdown|menu radius 8, dropdown|item height 40 with hover/focus states) plus the token layer: white trigger, border #E3E3E3, border-hover, bg-elevated panel, color-error danger, text-tertiary group title, radius-default 8; the flyout reuses the same elevated-panel + item tokens, and the open submenu-parent takes the brand-tinted bg-accent / color-primary highlight. Placement (bottomLeft/bottomRight/topLeft/topRight) + click/hover trigger from AntD Dropdown conventions; the flyout does edge-collision flip like AntD's rc-menu submenu. Motion bound to the shared motion tokens on persistent panels (root + each flyout); every glyph from the DS icon library."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "admin",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-dropdown> 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-dropdown.js';  // registers <aha-dropdown>\n</script>\n\n<!-- placement=\"bottomLeft\" (default) | \"bottomRight\" | \"topLeft\" | \"topRight\"\n     trigger=\"click\" (default) | \"hover\"\n     items: leaf {key,label,icon?,disabled?,danger?} · submenu-parent {key,label,icon?,children:[…]}\n            (opens an adjacent flyout — hover / ArrowRight / Enter; ArrowLeft / Escape closes it)\n            · {type:\"divider\"} · {type:\"group\",label,children:[]} -->\n<aha-dropdown id=\"menu\" label=\"Actions\" placement=\"bottomLeft\"\n  items='[\n    {\"key\":\"rename\",\"label\":\"Rename\",\"icon\":\"system-pencil-simple\"},\n    {\"key\":\"export\",\"label\":\"Export\",\"icon\":\"system-export\",\"children\":[\n      {\"key\":\"pdf\",\"label\":\"PDF\",\"icon\":\"system-file\"},\n      {\"key\":\"pptx\",\"label\":\"PowerPoint\",\"icon\":\"system-file\"},\n      {\"key\":\"xlsx\",\"label\":\"Excel\",\"icon\":\"system-file\"}\n    ]},\n    {\"type\":\"divider\"},\n    {\"key\":\"delete\",\"label\":\"Delete\",\"icon\":\"system-trash\",\"danger\":true}\n  ]'>\n</aha-dropdown>\n\n<script>\n  // Opens on click, closes on outside-click / Escape (focus returns to the trigger); choosing an\n  // item emits a composed `select`.\n  document.getElementById('menu').addEventListener('select', (e) => console.log('action', e.detail.key));\n</script>\n",
    "react": "import '@ahaslides-product/design/icons';        // registers <aha-icon> for leading + caret glyphs\nimport '@ahaslides-product/design/aha-dropdown';  // registers <aha-dropdown>\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 AhaDropdown({ label, items, placement, trigger, 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-dropdown ref={ref} label={label} placement={placement} trigger={trigger} items={JSON.stringify(items)} />;\n}\n\n// usage — leading icons, a submenu-parent (children[] → adjacent flyout), a divider, a red danger action\n// placement: bottomLeft | bottomRight | topLeft | topRight · trigger: click | hover\n// a submenu opens on hover / ArrowRight / Enter and closes on ArrowLeft / Escape (edge-collision flip)\n<AhaDropdown\n  label=\"Actions\"\n  placement=\"bottomLeft\"\n  items={[\n    { key: 'rename', label: 'Rename', icon: 'system-pencil-simple' },\n    { key: 'export', label: 'Export', icon: 'system-export', children: [\n      { key: 'pdf', label: 'PDF', icon: 'system-file' },\n      { key: 'pptx', label: 'PowerPoint', icon: 'system-file' },\n      { key: 'xlsx', label: 'Excel', icon: 'system-file' },\n    ] },\n    { type: 'divider' },\n    { key: 'delete', label: 'Delete', icon: 'system-trash', danger: true },\n  ]}\n  onSelect={(key) => runAction(key)}\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-dropdown';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue — leading icons, a submenu-parent (children[] → adjacent flyout), a divider, a danger action\n// placement: bottomLeft | bottomRight | topLeft | topRight · trigger: click | hover\n// a submenu opens on hover / ArrowRight / Enter and closes on ArrowLeft / Escape (edge-collision flip)\n<script setup>\nimport { computed } from 'vue';\nconst items = computed(() => JSON.stringify([\n  { key: 'rename', label: 'Rename', icon: 'system-pencil-simple' },\n  { key: 'export', label: 'Export', icon: 'system-export', children: [\n    { key: 'pdf', label: 'PDF', icon: 'system-file' },\n    { key: 'pptx', label: 'PowerPoint', icon: 'system-file' },\n    { key: 'xlsx', label: 'Excel', icon: 'system-file' },\n  ] },\n  { type: 'divider' },\n  { key: 'delete', label: 'Delete', icon: 'system-trash', danger: true },\n]));\nfunction onSelect(e) { runAction(e.detail.key); }\n</script>\n\n<template>\n  <aha-dropdown label=\"Actions\" placement=\"bottomLeft\" :items=\"items\" @select=\"onSelect\" />\n</template>\n"
  }
}
