{
  "generatedFrom": "badge.contract.json",
  "component": "Badge",
  "slug": "badge",
  "group": "Data Display",
  "tier": "leaf-lit",
  "summary": "A small count or status marker — notification counts, unread dots, a standalone status label.",
  "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-badge';",
    "element": "aha-badge",
    "registers": "aha-badge"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/badge/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/badge/badge.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/badge.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/badge.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-badge> 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-badge"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-badge"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-badge",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "count",
      "type": "number",
      "default": "0",
      "desc": "The number shown in the bubble; 0 hides it unless showZero"
    },
    {
      "name": "overflowCount",
      "type": "number",
      "default": "99",
      "desc": "Cap; above it renders as N+ (alias: max)"
    },
    {
      "name": "showZero",
      "type": "boolean",
      "default": "false",
      "desc": "Keep the bubble visible when count is 0"
    },
    {
      "name": "dot",
      "type": "boolean",
      "default": "false",
      "desc": "Render a bare marker, no number"
    },
    {
      "name": "status",
      "type": "success | processing | error | warning | default | primary",
      "default": "error",
      "desc": "Semantic colour; with `text` (no count/dot) renders a standalone dot + label"
    },
    {
      "name": "text",
      "type": "string",
      "default": "—",
      "desc": "Label for a standalone status badge"
    },
    {
      "name": "color",
      "type": "CSS colour",
      "default": "—",
      "desc": "Custom colour overriding the semantic default"
    },
    {
      "name": "ribbon",
      "type": "boolean",
      "default": "false",
      "desc": "Wrap the slotted child (a card) with a corner ribbon banner; `text` is the label, colour from color/status (default primary)"
    },
    {
      "name": "placement",
      "type": "start | end",
      "default": "end",
      "desc": "Ribbon corner: end = top-right (default), start = top-left"
    },
    {
      "name": "tone",
      "type": "danger | success | positive | essential | pro | branding | primary-alt",
      "default": "—",
      "desc": "Filled/tinted LABEL chip; each tone binds bg + fg to an --aha-* token"
    },
    {
      "name": "plan",
      "type": "free | edu | essential | pro | enterprise",
      "default": "—",
      "desc": "Plan preset chip (solid brand fill); pro/enterprise lead with a crown glyph"
    },
    {
      "name": "cycle",
      "type": "monthly | yearly",
      "default": "—",
      "desc": "Appended to a plan chip's default label (e.g. \"Pro · Yearly\")"
    },
    {
      "name": "rank",
      "type": "1 | 2 | 3 | 4",
      "default": "—",
      "desc": "Leaderboard rank chip; medal-tinted (gold/silver/bronze) with a leading system-Medal glyph"
    },
    {
      "name": "session",
      "type": "string",
      "default": "—",
      "desc": "Renders a \"Session N\" chip; the value is N (slotted content / `text` overrides the label)"
    },
    {
      "name": "href",
      "type": "URL",
      "default": "—",
      "desc": "Makes a tone/plan/rank/session chip a clickable LINK — renders <a class=\"chip\" href> (native focusable, animates on hover), never a <button>. Omit for a static <span> chip"
    }
  ],
  "tokens": [
    "color-error",
    "color-success",
    "color-warning",
    "color-primary",
    "text-tertiary",
    "text-inverse",
    "text-default",
    "bg-container",
    "radius-pill",
    "radius-xs",
    "bg-accent",
    "bg-positive",
    "bg-negative",
    "text-positive",
    "text-negative",
    "coral-10",
    "coral-60",
    "coral-70",
    "coral-80",
    "coral-20",
    "pink-10",
    "pink-70",
    "purple-15",
    "purple-55",
    "gray-30",
    "gray-20",
    "yellow-30",
    "yellow-90",
    "bg-dark",
    "text-secondary",
    "border-focus",
    "motion-mid",
    "ease-in-out"
  ],
  "spec": [
    {
      "label": "Count bubble",
      "value": "height 18 · min-width 18 · radius pill · padding 0 6 · scales in"
    },
    {
      "label": "On a child",
      "value": "floats to the child's top-right, 1px container-colour ring"
    },
    {
      "label": "Dot",
      "value": "8×8, no label"
    },
    {
      "label": "Status",
      "value": "standalone 6px dot + 14px label; processing pulses"
    },
    {
      "label": "Text",
      "value": "Plus Jakarta SemiBold 600, 11/18, #FFFFFF"
    },
    {
      "label": "Default colour",
      "value": "count/dot → error #F5222D; status → its semantic token; ribbon → primary #6A1EBB"
    },
    {
      "label": "Overflow / zero",
      "value": "count > overflowCount → N+; 0 hides unless showZero"
    },
    {
      "label": "Ribbon",
      "value": "corner banner over a wrapped card; 22px tall, radius 4, triangular fold; placement start/end"
    },
    {
      "label": "Tone chip",
      "value": "filled/tinted pill (radius pill, 22px); danger·success·positive·essential·pro·branding·primary-alt, each bound to an --aha token"
    },
    {
      "label": "Plan chip",
      "value": "preset (free·edu·essential·pro·enterprise); solid brand fill, cycle monthly/yearly; pro/enterprise lead with a crown"
    },
    {
      "label": "Rank chip",
      "value": "leaderboard 1st–4th, medal-tinted (gold/silver/bronze), leading system-Medal glyph"
    },
    {
      "label": "Session chip",
      "value": "\"Session N\" brand-accent pill; label from `session` / slotted text"
    },
    {
      "label": "Clickable = link",
      "value": "with `href` renders <a class=\"chip\"> (native focusable link, animates on hover) — never a <button>; no href = plain <span>"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Badge count/dot",
        "when": "a count or presence marker attached to another element (a bell, a tab, an avatar)"
      },
      {
        "what": "Badge status",
        "when": "a standalone semantic state label (Published, Generating, Failed) in a list or header"
      },
      {
        "what": "Tag",
        "when": "a standalone label/keyword chip with text but no status semantics"
      }
    ],
    "note": "A count/dot marks another element — wrap the element it belongs to so the pill anchors to its top-right. Keep counts meaningful; cap large numbers with overflowCount so the bubble stays small. Reserve the standalone status form for a semantic lifecycle state, and let processing pulse rather than spin a separate loader.",
    "dsv3": "COVERED — enumerated from the DS V3 Badge component-set taxonomy: Badge/Count, Badge/Dot, Badge/Status (状态), Badge/Text, Badge/On-Icon, Badge/On-Image, Badge/Dot-On-Text, Badge/Ribbon — i.e. the family carries a count pill, a bare dot, a standalone status dot+label, placement over a wrapped icon/image/text child, AND a corner ribbon banner wrapping a card. antd Badge conventions supply the count sizing (18px bubble, 8×8 dot, 11/18 SemiBold), overflowCount N+, showZero, the status set (success/processing/error/default/warning), and Badge.Ribbon (a corner banner, 22px tall, radius-xs on the outer corner, a darkened triangular fold, placement start/end, colour default primary). Token layer: count/dot default color-error, status→color-success/warning/primary(processing)/text-tertiary(default), ribbon default color-primary + radius-xs fold, radius-pill, text-inverse, bg-container ring. The measured component standard has no badge cell — a deliberate, documented extension, not a measured match."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "settings",
    "audience"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-badge> 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>\n  import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-badge.js'; // registers <aha-badge>\n</script>\n\n<div style=\"display:flex; gap:24px; align-items:center\">\n  <!-- count over a wrapped child, capped as N+ -->\n  <aha-badge count=\"128\" overflow-count=\"99\">\n    <aha-icon name=\"system-bell\" size=\"24\"></aha-icon>\n  </aha-badge>\n  <!-- a bare status dot -->\n  <aha-badge dot status=\"success\"></aha-badge>\n  <!-- a standalone status: a semantic dot + a label -->\n  <aha-badge status=\"processing\" text=\"Generating\"></aha-badge>\n</div>\n\n<div style=\"display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:24px\">\n  <!-- TONE — a filled/tinted label chip (danger·success·positive·essential·pro·branding·primary-alt) -->\n  <aha-badge tone=\"essential\">Essential</aha-badge>\n  <aha-badge tone=\"pro\">Pro</aha-badge>\n  <aha-badge tone=\"branding\">New</aha-badge>\n\n  <!-- PLAN preset chips — pro/enterprise lead with a crown; `cycle` appends the billing cycle -->\n  <aha-badge plan=\"free\"></aha-badge>\n  <aha-badge plan=\"pro\" cycle=\"yearly\"></aha-badge>\n\n  <!-- RANK (leaderboard) + SESSION -->\n  <aha-badge rank=\"1\"></aha-badge>\n  <aha-badge session=\"3\"></aha-badge>\n\n  <!-- CLICKABLE = a LINK (<a href>), never a <button>. Navigates to upgrade; animates on hover. -->\n  <aha-badge plan=\"pro\" href=\"/billing/upgrade\"></aha-badge>\n</div>\n\n<!-- ribbon — a corner banner wrapping a card (placement end = top-right, default) -->\n<aha-badge ribbon text=\"New\" style=\"display:inline-block; margin-top:24px\">\n  <div style=\"width:200px; padding:20px; border:1px solid var(--aha-border,#E3E3E3); border-radius:var(--aha-radius-default,8px); background:var(--aha-bg-container,#fff)\">\n    Quiz template\n  </div>\n</aha-badge>\n",
    "react": "import '@ahaslides-product/design/aha-badge';   // registers <aha-badge>\nimport '@ahaslides-product/design/icons';       // registers <aha-icon>\n\n// Attributes only — no events — so React 18/19 can render the element directly.\n// Boolean attrs (dot/showZero) are the attribute PRESENCE, so spread them conditionally.\nfunction AhaBadge({ count, overflowCount, dot, status, text, color, showZero, ribbon, placement,\n                    tone, plan, cycle, rank, session, href, children }) {\n  return (\n    <aha-badge\n      count={count}\n      overflow-count={overflowCount}\n      status={status}\n      text={text}\n      color={color}\n      placement={placement}\n      tone={tone}\n      plan={plan}\n      cycle={cycle}\n      rank={rank}\n      session={session}\n      href={href}\n      {...(dot ? { dot: '' } : {})}\n      {...(showZero ? { 'show-zero': '' } : {})}\n      {...(ribbon ? { ribbon: '' } : {})}\n    >\n      {children}\n    </aha-badge>\n  );\n}\n\n// count over a wrapped child\n<AhaBadge count={unread} overflowCount={99}>\n  <BellIcon />\n</AhaBadge>\n\n// a standalone status badge — a semantic dot + label\n<AhaBadge status=\"processing\" text=\"Generating\" />\n\n// a corner ribbon banner wrapping a card (placement start/end)\n<AhaBadge ribbon text=\"New\">\n  <TemplateCard />\n</AhaBadge>\n\n// a tinted TONE label chip\n<AhaBadge tone=\"essential\">Essential</AhaBadge>\n\n// a PLAN preset chip (pro/enterprise show a crown; cycle appends the billing cycle)\n<AhaBadge plan=\"pro\" cycle=\"yearly\" />\n\n// a leaderboard RANK chip + a SESSION chip\n<AhaBadge rank={1} />\n<AhaBadge session={3} />\n\n// CLICKABLE = a LINK (<a href>), NOT a button — navigates to upgrade, animates on hover\n<AhaBadge plan=\"pro\" href=\"/billing/upgrade\" />\n",
    "vue": "// main.ts — register the elements + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-badge';\nimport '@ahaslides-product/design/icons';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst unread = ref(128);\n</script>\n\n<template>\n  <!-- count over a wrapped child, capped as N+ -->\n  <aha-badge :count=\"unread\" overflow-count=\"99\">\n    <aha-icon name=\"system-bell\" size=\"24\" />\n  </aha-badge>\n\n  <!-- a standalone status badge — a semantic dot + label -->\n  <aha-badge status=\"processing\" text=\"Generating\" />\n\n  <!-- a corner ribbon banner wrapping a card (placement start/end) -->\n  <aha-badge ribbon text=\"New\">\n    <div class=\"template-card\">Quiz template</div>\n  </aha-badge>\n\n  <!-- a tinted TONE label chip -->\n  <aha-badge tone=\"essential\">Essential</aha-badge>\n\n  <!-- a PLAN preset chip (pro/enterprise show a crown; cycle appends the billing cycle) -->\n  <aha-badge plan=\"pro\" cycle=\"yearly\" />\n\n  <!-- a leaderboard RANK chip + a SESSION chip -->\n  <aha-badge rank=\"1\" />\n  <aha-badge session=\"3\" />\n\n  <!-- CLICKABLE = a LINK (<a href>), NOT a button — navigates to upgrade, animates on hover -->\n  <aha-badge plan=\"pro\" href=\"/billing/upgrade\" />\n</template>\n"
  }
}
