{
  "generatedFrom": "tag.contract.json",
  "component": "Tag",
  "slug": "tag",
  "group": "Data Display",
  "tier": "leaf-lit",
  "summary": "A small label chip for a category, keyword, or state — filled or bordered, with an icon, a dismiss ×, or a checkable toggle.",
  "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-tag';",
    "element": "aha-tag",
    "registers": "aha-tag"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/tag/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/tag/tag.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/tag.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/tag.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-tag> 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-tag"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-tag"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-tag",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "color",
      "type": "neutral | primary | success | processing | warning | error",
      "default": "neutral",
      "desc": "Semantic tone preset (alias: `variant`)"
    },
    {
      "name": "bordered",
      "type": "boolean",
      "default": "false",
      "desc": "Outlined shape — white surface + a coloured 1px hairline instead of the filled tint"
    },
    {
      "name": "icon",
      "type": "string",
      "default": "—",
      "desc": "Leading DS icon name, e.g. system-tag (resolved via <aha-icon>)"
    },
    {
      "name": "closable",
      "type": "boolean",
      "default": "false",
      "desc": "Shows a dismiss × that animates out, removes the tag, and emits close"
    },
    {
      "name": "checkable",
      "type": "boolean",
      "default": "false",
      "desc": "Renders a selectable toggle chip (button, aria-pressed)"
    },
    {
      "name": "checked",
      "type": "boolean",
      "default": "false",
      "desc": "Selected state of a checkable tag (brand fill)"
    },
    {
      "name": "close",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed event when the × is pressed"
    },
    {
      "name": "change",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed event on a checkable toggle, detail { checked }"
    }
  ],
  "tokens": [
    "gray-30",
    "text-secondary",
    "bg-container",
    "border",
    "purple-10",
    "purple-60",
    "text-inverse",
    "bg-positive",
    "text-positive",
    "border-success",
    "bg-informative",
    "text-link",
    "border-info",
    "bg-warning",
    "text-warning",
    "border-warning",
    "bg-negative",
    "text-negative",
    "border-error",
    "border-focus",
    "radius-xs"
  ],
  "spec": [
    {
      "label": "Box",
      "value": "height 22 · radius 4 · padding 0 8 · 6px gap to a 14px leading icon"
    },
    {
      "label": "Text",
      "value": "Plus Jakarta SemiBold 600, 12/18"
    },
    {
      "label": "Neutral",
      "value": "bg #F1F1F1, label #4A4A4A"
    },
    {
      "label": "Primary",
      "value": "bg #F9F5FF, label #6A1EBB"
    },
    {
      "label": "Success / Processing / Warning / Error",
      "value": "tinted bg + matching text token"
    },
    {
      "label": "Bordered",
      "value": "white surface + a coloured 1px hairline (the outlined DS V3 flavour)"
    },
    {
      "label": "Closable",
      "value": "system-x ✕; animates out (scale + fade, motion-mid) then removes + emits close"
    },
    {
      "label": "Checkable",
      "value": "outlined toggle; checked → brand fill #6A1EBB / white label; aria-pressed synced"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Tag",
        "when": "labelling content with a category/keyword, or showing a non-urgent state inline"
      },
      {
        "what": "Badge",
        "when": "a count or a status dot attached to another element"
      },
      {
        "what": "Button",
        "when": "the chip should trigger an action — a Tag is a label, not a control"
      }
    ],
    "note": "Keep tag text to one or two words, sentence case. Colour carries meaning — reserve success/processing/warning/error for real states, not decoration. Use `closable` for user-removable keywords; use `checkable` for an inline filter toggle. For a domain lifecycle state prefer the status pill (aha-design-status-badges).",
    "dsv3": "COVERED — enumerated from the DS V3 Tag component sets (`Tag` + `Tag/Outlined`, fileKey MwjvUjVI0HnfwD9EwPXRAy) and the measured `tag|*` / `tagz|*` reference cells. The measured `tag` cell is an OUTLINED chip (radius 4, 1px border #D3D7E1, rest→hover→active tinting toward color-primary) — captured here as the `bordered` shape. The filled tint flavour (neutral/primary/success/processing/warning/error) is the DS label-chip form derived from the bg/text positive·informative·warning·negative token families. `icon` (leading system glyph), `closable` (system-x ✕ with animate-out + composed close), and `checkable` (the selectable toggle with a brand-fill checked state + aria-pressed + composed change) complete the matrix. Radius from the 4/6/8/12/16 scale (radius-xs). Motion bound to the shared motion tokens on persistent nodes — the checked flip and the close collapse both animate; the checked toggle syncs on the persistent chip (no subtree rebuild). Not-yet-modelled: the five discrete tagz sizes (xs/sm/md/lg/xl) — this element ships the single h22 label size; a `size` axis is the follow-up if product needs it."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "settings",
    "audience"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-tag> 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     Icons are summoned by name from the DS icon library via <aha-icon>. -->\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>\n  import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-tag.js';  // registers <aha-tag>\n</script>\n\n<div style=\"display:flex; gap:8px; align-items:center; flex-wrap:wrap\">\n  <aha-tag color=\"primary\" icon=\"system-tag\">Poll</aha-tag>\n  <aha-tag color=\"success\" icon=\"system-check\">Published</aha-tag>\n  <aha-tag bordered color=\"processing\">Scheduled</aha-tag>\n  <aha-tag closable id=\"kw\">Marketing</aha-tag>\n  <aha-tag checkable checked id=\"filter\">Quizzes only</aha-tag>\n</div>\n\n<script>\n  // closable: fires `close`, then the tag animates out + removes itself\n  document.getElementById('kw').addEventListener('close', () => console.log('removed'));\n  // checkable: fires `change` with { checked }\n  document.getElementById('filter').addEventListener('change', (e) => console.log('checked', e.detail.checked));\n</script>\n",
    "react": "import '@ahaslides-product/design/icons';   // registers <aha-icon>\nimport '@ahaslides-product/design/aha-tag'; // registers <aha-tag>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper for the composed `close` / `change` events; React 19 can bind directly.\nfunction AhaTag({ color, bordered, icon, closable, checkable, checked, onClose, onChange, children }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    const c = () => onClose?.();\n    const ch = (e) => onChange?.(e.detail.checked);\n    el.addEventListener('close', c);\n    el.addEventListener('change', ch);\n    return () => { el.removeEventListener('close', c); el.removeEventListener('change', ch); };\n  });\n  return (\n    <aha-tag ref={ref} color={color} icon={icon}\n      {...(bordered ? { bordered: '' } : {})}\n      {...(closable ? { closable: '' } : {})}\n      {...(checkable ? { checkable: '' } : {})}\n      {...(checked ? { checked: '' } : {})}\n    >{children}</aha-tag>\n  );\n}\n\n// usage\n<AhaTag color=\"primary\" icon=\"system-tag\">Poll</AhaTag>\n<AhaTag color=\"success\" bordered icon=\"system-check\">Published</AhaTag>\n<AhaTag closable onClose={() => removeKeyword('Marketing')}>Marketing</AhaTag>\n<AhaTag checkable checked onChange={(on) => setFilter(on)}>Quizzes only</AhaTag>\n",
    "vue": "// main.ts — register the elements + mark aha-* as custom elements\nimport '@ahaslides-product/design/icons';    // registers <aha-icon>\nimport '@ahaslides-product/design/aha-tag';  // registers <aha-tag>\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst keywords = ref(['Poll', 'Marketing']);\nconst quizzesOnly = ref(true);\n</script>\n\n<template>\n  <aha-tag color=\"primary\" icon=\"system-tag\">Poll</aha-tag>\n  <aha-tag color=\"success\" bordered icon=\"system-check\">Published</aha-tag>\n  <aha-tag\n    closable\n    @close=\"keywords = keywords.filter(k => k !== 'Marketing')\"\n  >Marketing</aha-tag>\n  <aha-tag\n    checkable\n    :checked=\"quizzesOnly || undefined\"\n    @change=\"quizzesOnly = $event.detail.checked\"\n  >Quizzes only</aha-tag>\n</template>\n"
  }
}
