{
  "generatedFrom": "rate.contract.json",
  "component": "Rate",
  "slug": "rate",
  "group": "Data Entry",
  "tier": "leaf-lit",
  "summary": "Capture or display a score as a row of stars.",
  "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-rate';",
    "element": "aha-rate",
    "registers": "aha-rate"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/rate/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/rate/rate.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/rate.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/rate.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-rate> 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-rate"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-rate"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-rate",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "value",
      "type": "number",
      "default": "0",
      "desc": "Current score (0…count; .5 steps with allow-half)"
    },
    {
      "name": "count",
      "type": "number",
      "default": "5",
      "desc": "Number of stars (DS V3 name; `max` is an alias)"
    },
    {
      "name": "allow-half",
      "type": "boolean",
      "default": "false",
      "desc": "Half-star selection — the left half of a star scores n−0.5"
    },
    {
      "name": "allow-clear",
      "type": "boolean",
      "default": "false",
      "desc": "Click the current value again to clear to 0"
    },
    {
      "name": "icon",
      "type": "string",
      "default": "—",
      "desc": "Custom character: a DS icon name (e.g. system-heart-straight) drawn instead of the star"
    },
    {
      "name": "readonly",
      "type": "boolean",
      "default": "false",
      "desc": "Display only — no interaction"
    },
    {
      "name": "disabled",
      "type": "boolean",
      "default": "false",
      "desc": "Dims and disables the control"
    },
    {
      "name": "change",
      "type": "CustomEvent<{value}>",
      "default": "—",
      "desc": "Composed event; read e.detail.value"
    }
  ],
  "tokens": [
    "yellow-50",
    "gray-40",
    "color-primary",
    "radius-xs",
    "font-product",
    "motion-mid",
    "motion-fast",
    "ease-in-out",
    "ease-out"
  ],
  "spec": [
    {
      "label": "Star",
      "value": "24×24, gap 4"
    },
    {
      "label": "Filled",
      "value": "yellow-50 #FFE32C"
    },
    {
      "label": "Empty",
      "value": "gray-40 #E3E3E3"
    },
    {
      "label": "Half",
      "value": "filled overlay clipped to 50% (allow-half)"
    },
    {
      "label": "Character",
      "value": "any DS icon by name via `icon` (outline, brand-tinted)"
    },
    {
      "label": "Hover",
      "value": "scale 1.12, fill preview to cursor (half-precise)"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Rate",
        "when": "a subjective score on a small fixed scale — a review, a difficulty, satisfaction"
      },
      {
        "what": "Segmented",
        "when": "picking one labelled option from a few, not a magnitude"
      },
      {
        "what": "Slider",
        "when": "a continuous value across a wide range"
      }
    ],
    "note": "Keep the scale small (5 is the norm). Pair with a short label — 'Rate this session' — and show the chosen value nearby for confirmation.",
    "dsv3": "COVERED — from the DS V3 `rate` component-set: count (default 5), value + hover preview, allow-half, disabled/read-only display, allow-clear, and a custom character (any DS icon by name). Filled star uses yellow-50 (#FFE32C), the on-palette warm accent; empty uses gray-40 (#E3E3E3), the neutral border value. Half fill clips a persistent overlay glyph to 50%. Motion via the shared motion tokens on persistent nodes — no subtree rebuild on selection or hover."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "audience"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-rate> is the SAME shared custom element React and Vue consume.\n     Initial score is declarative (`value`); it emits a composed `change` event. -->\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-rate.js';   // registers <aha-rate>\n</script>\n\n<aha-rate id=\"score\" value=\"3\" count=\"5\"></aha-rate>\n<!-- half-step selection:  <aha-rate value=\"3.5\" count=\"5\" allow-half></aha-rate> -->\n<!-- custom DS character:  <aha-rate value=\"4\" icon=\"system-heart-straight\"></aha-rate> -->\n<!-- read-only display:    <aha-rate value=\"4\" readonly></aha-rate> -->\n\n<script>\n  // Plain DOM — no framework. Read e.detail.value off the composed CustomEvent.\n  document.getElementById('score').addEventListener('change', (e) => {\n    console.log('rating:', e.detail.value);\n  });\n</script>\n",
    "react": "import '@ahaslides-product/design/aha-rate';   // registers <aha-rate>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper; React 19 can use <aha-rate> directly.\nfunction AhaRate({ value, count = 5, allowHalf, icon, readonly, disabled, onChange }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    el.value = value ?? 0;\n    const h = (e) => onChange?.(e.detail.value);   // composed CustomEvent\n    el.addEventListener('change', h);\n    return () => el.removeEventListener('change', h);\n  });\n  return <aha-rate ref={ref} count={count} icon={icon || undefined}\n    allow-half={allowHalf || undefined} readonly={readonly || undefined} disabled={disabled || undefined} />;\n}\n\n// usage — whole-star, half-step, and a custom DS character\n<AhaRate value={score} onChange={setScore} />\n<AhaRate value={3.5} allowHalf onChange={setScore} />\n<AhaRate value={4} icon=\"system-heart-straight\" onChange={setScore} />\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-rate';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst score = ref(3);\n</script>\n\n<template>\n  <aha-rate\n    :value.prop=\"score\"\n    :count=\"5\"\n    @change=\"score = $event.detail.value\"\n  />\n  <!-- half-step + a custom DS character -->\n  <aha-rate :value.prop=\"score\" :count=\"5\" allow-half @change=\"score = $event.detail.value\" />\n  <aha-rate :value.prop=\"score\" icon=\"system-heart-straight\" @change=\"score = $event.detail.value\" />\n</template>\n"
  }
}
