{
  "generatedFrom": "popover.contract.json",
  "component": "Popover",
  "slug": "popover",
  "group": "Overlays",
  "tier": "leaf-lit",
  "summary": "Anchor a floating card of rich content — a title, a body, and an optional arrow — to a trigger.",
  "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-popover';",
    "element": "aha-popover",
    "registers": "aha-popover"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/popover/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/popover/popover.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/popover.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/popover.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-popover> 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-popover"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-popover"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-popover",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "open",
      "type": "boolean",
      "default": "false",
      "desc": "Controlled open state; reflected attribute"
    },
    {
      "name": "placement",
      "type": "top | bottom | left | right | top-start | top-end | bottom-start | bottom-end | left-start | left-end | right-start | right-end",
      "default": "bottom",
      "desc": "Side the panel anchors to. The 4 cardinal values align the panel to the trigger's leading edge; the 8 edge-aligned values (…-start / …-end) pin it to the trigger's start/end edge with the arrow offset near that edge (AntD naming)"
    },
    {
      "name": "trigger",
      "type": "click | hover | focus",
      "default": "click",
      "desc": "How the panel opens. Click closes on outside-click + Escape (focus returns); hover/focus track pointer/focus"
    },
    {
      "name": "title",
      "type": "string",
      "default": "—",
      "desc": "Bold header rendered above the body (hidden when empty)"
    },
    {
      "name": "content",
      "type": "string",
      "default": "—",
      "desc": "Text body alternative to slotted content (used when nothing is slotted)"
    },
    {
      "name": "arrow",
      "type": "boolean",
      "default": "false",
      "desc": "Show the pointer arrow at the panel edge"
    },
    {
      "name": "[slot=trigger]",
      "type": "slotted element",
      "default": "—",
      "desc": "The element that toggles the panel"
    },
    {
      "name": "open / close",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed events on show / hide"
    }
  ],
  "tokens": [
    "bg-elevated",
    "text-default",
    "text-secondary",
    "border",
    "radius-default",
    "font-product",
    "motion-mid",
    "ease-out"
  ],
  "spec": [
    {
      "label": "Panel",
      "value": "bg-elevated #FFFFFF, 1px border #E3E3E3, radius 8, min-width 180 / max 280"
    },
    {
      "label": "Elevation",
      "value": "0 6px 16px ink 12%"
    },
    {
      "label": "Title",
      "value": "SemiBold 600, 14/21 text-default, 6px above the body"
    },
    {
      "label": "Body",
      "value": "pad 12 16, 14/21 text-secondary #4A4A4A (text-default when title-less)"
    },
    {
      "label": "Placement",
      "value": "top · bottom · left · right (8px offset), PLUS 8 edge-aligned (top-start/-end, bottom-start/-end, left-start/-end, right-start/-end) — same side, aligned to the trigger edge with the arrow offset near it"
    },
    {
      "label": "Trigger",
      "value": "click (default) · hover · focus"
    },
    {
      "label": "Arrow",
      "value": "optional 8px pointer at the panel edge, matches the surface + border"
    },
    {
      "label": "Motion",
      "value": "fade + 4px directional lift on open (ease-out, persistent node)"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Popover",
        "when": "a small panel of rich or interactive content anchored to a control"
      },
      {
        "what": "Tooltip",
        "when": "a short, non-interactive hint on hover"
      },
      {
        "what": "Modal",
        "when": "a focused task that should block the rest of the page"
      }
    ],
    "note": "Trigger on click for interactive content (Tooltip owns hover). Give it a short `title` and a one-line body; a long form belongs in a Modal. A click trigger closes on outside-click and Escape (focus returns to the trigger) — both built in.",
    "dsv3": "COVERED — from the DS V3 Popover component-set ('the floating card popped by clicking or hovering'), matching the antd Popover matrix: a `title` header over a body (unlike the Tooltip's plain hint), the FULL 12-placement set — the 4 cardinal sides (top · bottom · left · right) PLUS the 8 edge-aligned ones (top-start, top-end, bottom-start, bottom-end, left-start, left-end, right-start, right-end — same side, aligned to the trigger's start/end edge with the arrow offset near that edge, AntD naming) — `trigger` (click · hover · focus) and a toggleable `arrow`. White elevated surface, standard 8 radius, soft ink shadow; title SemiBold text-default over a text-secondary body. Fade + 4px directional lift via the shared motion tokens on a persistent panel — no subtree rebuild on open/close."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-popover> is the SAME shared custom element React and Vue consume.\n     Put the anchor in slot=\"trigger\"; add a `title` header + slotted (or `content`) body.\n     placement = top | bottom | left | right (centred) OR edge-aligned top-start | top-end |\n       bottom-start | bottom-end | left-start | left-end | right-start | right-end\n       (same side, aligned to the trigger's start/end edge, arrow offset near it)\n     · trigger = click | hover | focus · `arrow` toggles the pointer.\n     A click trigger closes on outside-click and Escape (focus returns to the trigger); emits composed open / close. -->\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/aha-popover.js';   // registers <aha-popover>\n</script>\n\n<!-- bottom-end: anchored under the trigger, aligned to its right edge, arrow near that edge -->\n<aha-popover id=\"pop\" placement=\"bottom-end\" trigger=\"click\" arrow title=\"Share this deck\">\n  <button slot=\"trigger\">Options</button>\n  <div>\n    <p style=\"margin:0 0 8px\">Share, rename or export this deck.</p>\n    <a href=\"#\">Manage access</a>\n  </div>\n</aha-popover>\n\n<script>\n  document.getElementById('pop').addEventListener('open', () => console.log('opened'));\n</script>\n",
    "react": "import '@ahaslides-product/design/aha-popover';   // registers <aha-popover>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper; React 19 can use <aha-popover> directly.\n// placement: top | bottom | left | right (centred) OR edge-aligned top-start | top-end |\n//   bottom-start | bottom-end | left-start | left-end | right-start | right-end\nfunction AhaPopover({ placement = 'bottom', trigger = 'click', arrow = false, title, onOpenChange, anchor, children }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    const o = () => onOpenChange?.(true);\n    const c = () => onOpenChange?.(false);\n    el.addEventListener('open', o);\n    el.addEventListener('close', c);\n    return () => { el.removeEventListener('open', o); el.removeEventListener('close', c); };\n  });\n  return (\n    <aha-popover ref={ref} placement={placement} trigger={trigger} title={title} {...(arrow ? { arrow: '' } : {})}>\n      <span slot=\"trigger\">{anchor}</span>\n      {children}\n    </aha-popover>\n  );\n}\n\n// usage — edge-aligned: aligned to the trigger's right edge, arrow near it\n<AhaPopover anchor={<button>Options</button>} title=\"Share this deck\" placement=\"bottom-end\" arrow trigger=\"click\">\n  <a href=\"#\">Manage access</a>\n</AhaPopover>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-popover';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue — placement: top|bottom|left|right (centred) OR edge-aligned\n//   top-start|top-end|bottom-start|bottom-end|left-start|left-end|right-start|right-end\n<template>\n  <aha-popover placement=\"bottom-end\" trigger=\"click\" arrow title=\"Share this deck\" @open=\"onOpen\" @close=\"onClose\">\n    <button slot=\"trigger\">Options</button>\n    <div>\n      <p>Share, rename or export this deck.</p>\n      <a href=\"#\">Manage access</a>\n    </div>\n  </aha-popover>\n</template>\n"
  }
}
