{
  "generatedFrom": "spin.contract.json",
  "component": "Spin",
  "slug": "spin",
  "group": "Feedback",
  "tier": "leaf-lit",
  "summary": "A brand-coloured spinner for an indeterminate wait — loading a page, a panel, or results.",
  "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-spin';",
    "element": "aha-spin",
    "registers": "aha-spin"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/spin/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/spin/spin.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/spin.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/spin.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-spin> 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-spin"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-spin"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-spin",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "size",
      "type": "small | default | large",
      "default": "default",
      "desc": "Spinner diameter"
    },
    {
      "name": "tip",
      "type": "string",
      "default": "—",
      "desc": "Optional caption shown under the ring"
    },
    {
      "name": "spinning",
      "type": "boolean",
      "default": "false",
      "desc": "Wrapper mode only (with slotted children): show/hide the overlay scrim + centred ring. Toggling it flips a class on a persistent node — the child subtree is never rebuilt."
    }
  ],
  "tokens": [
    "color-primary",
    "purple-20",
    "text-secondary"
  ],
  "spec": [
    {
      "label": "Ring",
      "value": "20×20 · 2px · track purple-20, head primary #6A1EBB"
    },
    {
      "label": "Sizes",
      "value": "small 14 · default 20 · large 32"
    },
    {
      "label": "Motion",
      "value": "rotate 0.9s linear infinite (respects reduced-motion); scrim fade + blur via motion-mid"
    },
    {
      "label": "Tip",
      "value": "optional caption below (standalone) or under the centred ring (wrapper), text-secondary"
    },
    {
      "label": "Wrapper",
      "value": "slot children; when spinning, scrim = bg-container @60% over blurred child, ring centred"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Spin",
        "when": "an indeterminate wait with no measurable percentage"
      },
      {
        "what": "Progress",
        "when": "you can measure completion (a percentage)"
      },
      {
        "what": "Skeleton",
        "when": "you want to hold the shape of the content that is loading"
      }
    ],
    "note": "Reach for Spin only when you cannot show a percentage or hold the layout. Keep one spinner per waiting region; pair it with a short tip for waits over ~1s. To block a region while it loads, wrap the content and toggle `spinning` rather than swapping it for a bare centred spinner — the layout holds and the child stays mounted.",
    "dsv3": "COVERED — the DS V3 Spin component-set (componentKey 0f625bc383160251659708805c0382aa8acd4d68, filePath design_systems/Design System V3/components/_Spin_) has no measured cell; spec derived from the token layer (head color-primary, track purple-20) and antd Spin conventions: size (small/default/large), optional `tip`, a `spinning` toggle, and the WRAPPER mode (spinner overlays slotted content behind a dimmed scrim). A deliberate, documented extension."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "audience"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-spin> 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/aha-spin.js';   // registers <aha-spin>\n</script>\n\n<div style=\"display:flex; gap:32px; align-items:center\">\n  <aha-spin size=\"small\"></aha-spin>\n  <aha-spin></aha-spin>\n  <aha-spin size=\"large\" tip=\"Loading results…\"></aha-spin>\n</div>\n\n<!-- Wrapper mode — overlay a spinner over content while it loads.\n     Slot the content; toggle `spinning` to show/hide the dimmed scrim + centred ring. -->\n<aha-spin spinning tip=\"Loading…\" style=\"margin-top:24px\">\n  <div style=\"width:240px; padding:16px; border:1px solid var(--aha-border); border-radius:8px\">\n    Live results — votes are being tallied.\n  </div>\n</aha-spin>\n",
    "react": "import '@ahaslides-product/design/aha-spin';   // registers <aha-spin>\n\n// Attributes only — no events — so React 18/19 can render the element directly.\n// `spinning` is a boolean attribute: pass it (any value) to show, omit to hide.\nfunction AhaSpin({ size, tip, spinning, children }) {\n  return <aha-spin size={size} tip={tip} {...(spinning ? { spinning: '' } : {})}>{children}</aha-spin>;\n}\n\n// Standalone — a bare waiting region\n{loading ? <AhaSpin tip=\"Loading results…\" /> : <Results data={data} />}\n\n// Wrapper — keep the content mounted, overlay a scrim while it loads\n<AhaSpin spinning={loading} tip=\"Loading…\">\n  <ResultsPanel data={data} />\n</AhaSpin>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-spin';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst loading = ref(true);\n</script>\n\n<template>\n  <!-- Standalone -->\n  <aha-spin v-if=\"loading\" size=\"large\" tip=\"Loading results…\" />\n\n  <!-- Wrapper — slot content, toggle `spinning` (boolean attr) to overlay the scrim -->\n  <aha-spin :spinning=\"loading || null\" tip=\"Loading…\">\n    <ResultsPanel :data=\"data\" />\n  </aha-spin>\n</template>\n"
  }
}
