{
  "generatedFrom": "csat.contract.json",
  "component": "CSAT",
  "slug": "csat",
  "group": "AhaSlides surfaces",
  "tier": "leaf-lit",
  "summary": "The shared thumbs up/down satisfaction prompt — the one CSAT control, not a bespoke rating.",
  "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-csat';",
    "element": "aha-csat",
    "registers": "aha-csat"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/csat/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/csat/csat.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/csat.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/csat.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-csat> 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-csat"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-csat"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-csat",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "prompt",
      "type": "string",
      "default": "—",
      "desc": "Optional question shown before the thumbs"
    },
    {
      "name": "source",
      "type": "string",
      "default": "—",
      "desc": "Stable placement token for analytics — passed in the rate and feedback events"
    },
    {
      "name": "value",
      "type": "up | down",
      "default": "—",
      "desc": "The current rating (also reflects a click); syncs the selected thumb's aria-pressed"
    },
    {
      "name": "thanks",
      "type": "string",
      "default": "—",
      "desc": "Opt-in thank-you line — shown (role=status) after rating (on down, after feedback submit); fades in on a persistent node"
    },
    {
      "name": "feedback-prompt",
      "type": "string",
      "default": "What could be better?",
      "desc": "Heading shown at the top of the thumbs-down feedback popover"
    },
    {
      "name": "feedback-placeholder",
      "type": "string",
      "default": "Tell us more (optional)",
      "desc": "Placeholder for the free-text field in the feedback popover"
    }
  ],
  "tokens": [
    "text-secondary",
    "text-default",
    "icon-muted",
    "color-primary",
    "color-success"
  ],
  "spec": [
    {
      "label": "Layout",
      "value": "borderless single row (21px high): 14px #4A4A4A prompt + 16px thumb icons, no chrome — the only layout (legacy `inline` is a no-op)"
    },
    {
      "label": "Rest",
      "value": "icon #8A8A8A"
    },
    {
      "label": "Hover",
      "value": "scale 1.12 · icon primary #6A1EBB (motion tokens)"
    },
    {
      "label": "Selected",
      "value": "icon primary #6A1EBB · aria-pressed synced"
    },
    {
      "label": "Thumbs-up",
      "value": "rates instantly — reflects value='up', emits `rate`"
    },
    {
      "label": "Thumbs-down",
      "value": "registers value='down' + emits `rate`, then opens a feedback popover (shared <aha-popover>, role=dialog · Esc/outside-click close) anchored to the down thumb — a prompt, an <aha-counted-textarea> and a primary Send button; dismissing keeps the down rating"
    },
    {
      "label": "Thank-you",
      "value": "opt-in `thanks` — check + success #16C49A line fades in (motion tokens): instantly on up, after feedback submit on down"
    },
    {
      "label": "Events",
      "value": "`rate` → { rating: 'up' | 'down', source } · `feedback` → { rating: 'down', source, feedback }"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "CSAT",
        "when": "a lightweight satisfaction check on a feature or a piece of content"
      },
      {
        "what": "Result",
        "when": "a full-block outcome after an operation"
      },
      {
        "what": "a Form",
        "when": "you need structured, multi-field feedback with validation"
      }
    ],
    "note": "Use the shared control — never build a bespoke rating. Always pass a stable `source` per placement so segmentation stays typo-proof. Keep it to the binary thumbs; free-text is asked for ONLY after a thumbs-down (the built-in feedback popover), and never block rendering on tracking.",
    "dsv3": "COVERED — this is the reusable component form of the feedback pattern's CSAT rule (parts/feedback.guide.md): a binary thumbs widget with a required source token, and the 'ask for free-text only after a thumbs-down' follow-up realised by reusing <aha-popover> + <aha-counted-textarea> + <aha-button>. The dedicated `feedback` event maps to the pattern's distinct CSAT_FEEDBACK_SUBMITTED analytics event (separate from CSAT_RATED). Colours + motion map to the token layer. A deliberate, documented AhaSlides surface."
  },
  "surfaces": [
    "editor",
    "dashboard",
    "audience"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-csat> is the SAME shared custom element React and Vue consume — here in its native\n     form. Its thumbs are the shared <aha-icon>, and a thumbs-down opens a feedback popover built\n     from the shared <aha-popover> + <aha-counted-textarea> + <aha-button> — all loaded by the\n     element itself. 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-csat.js';   // registers <aha-csat> (+ <aha-icon>, <aha-popover>, <aha-counted-textarea>, <aha-button>)\n</script>\n\n<!-- The borderless single row (the only layout): prompt + 16px thumbs on one line, no chrome.\n     Thumbs-up rates instantly; thumbs-down opens the free-text feedback popover.\n     `thanks` opts into the confirmation line, shown after the up rating / feedback submit. -->\n<aha-csat prompt=\"How's your experience?\" source=\"docs_footer\" thanks=\"Thanks for the feedback!\"></aha-csat>\n\n<script>\n  const csat = document.querySelector('aha-csat');\n  csat.addEventListener('rate', (e) => {\n    console.log('CSAT', e.detail.rating, 'from', e.detail.source);   // { rating: 'up' | 'down', source }\n  });\n  csat.addEventListener('feedback', (e) => {\n    console.log('CSAT feedback', e.detail.feedback, 'from', e.detail.source);   // { rating: 'down', source, feedback }\n  });\n</script>\n",
    "react": "import { useEffect, useRef } from 'react';\nimport '@ahaslides-product/design/aha-csat';   // registers <aha-csat> (+ <aha-icon>, <aha-popover>, <aha-counted-textarea>, <aha-button>)\n\n// The element emits composed `rate` and `feedback` events — attach ref listeners (React <19 pattern).\nfunction AhaCsat({ prompt, source, onRate, onFeedback }) {\n  const ref = useRef(null);\n  useEffect(() => {\n    const el = ref.current;\n    const rate = (e) => onRate?.(e.detail);         // { rating: 'up' | 'down', source }\n    const feedback = (e) => onFeedback?.(e.detail);  // { rating: 'down', source, feedback }\n    el.addEventListener('rate', rate);\n    el.addEventListener('feedback', feedback);\n    return () => { el.removeEventListener('rate', rate); el.removeEventListener('feedback', feedback); };\n  }, [onRate, onFeedback]);\n  return <aha-csat ref={ref} prompt={prompt} source={source} />;\n}\n\n// usage — thumbs-up rates instantly; thumbs-down opens the free-text feedback popover\n<AhaCsat\n  prompt=\"How's your experience?\"\n  source=\"editor_help\"\n  onRate={(d) => track('CSAT_RATED', d)}\n  onFeedback={(d) => track('CSAT_FEEDBACK_SUBMITTED', d)}\n/>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-csat';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue — the custom element's `rate` and `feedback` events bind natively with @rate / @feedback\n<script setup>\nfunction onRate(e) {\n  track('CSAT_RATED', e.detail);   // { rating: 'up' | 'down', source }\n}\nfunction onFeedback(e) {\n  track('CSAT_FEEDBACK_SUBMITTED', e.detail);   // { rating: 'down', source, feedback }\n}\n</script>\n\n<template>\n  <!-- The borderless single row (the only layout). Thumbs-up rates instantly; thumbs-down opens the\n       free-text feedback popover, which emits `feedback` on submit. -->\n  <aha-csat prompt=\"How's your experience?\" source=\"editor_help\" @rate=\"onRate\" @feedback=\"onFeedback\" />\n</template>\n"
  }
}
