{
  "generatedFrom": "question-list.contract.json",
  "component": "Question list",
  "slug": "question-list",
  "group": "Settings",
  "tier": "leaf-lit",
  "summary": "An editable list of collapsible questions — each a prompt field plus its OptionRow choices — with a full-width \"+ Add question\" that disables at max; composes NumberedItem + OptionRow (SETTINGS-31/36).",
  "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-question-list';",
    "element": "aha-question-list",
    "registers": "aha-question-list"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/question-list/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/question-list/question-list.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/question-list.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/question-list.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-question-list> 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-question-list"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-question-list"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-question-list",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "questions",
      "type": "Array<{id,prompt,options:[{id,text,correct}]}>",
      "default": "[]",
      "desc": "The model; set as a property or a JSON attribute"
    },
    {
      "name": "min",
      "type": "number",
      "default": "1",
      "desc": "Minimum questions — delete disables at this count"
    },
    {
      "name": "max",
      "type": "number",
      "default": "∞",
      "desc": "Maximum questions — +Add disables at this count"
    },
    {
      "name": "minoptions",
      "type": "number",
      "default": "2",
      "desc": "Minimum options per question — option delete disables at this count"
    },
    {
      "name": "maxoptions",
      "type": "number",
      "default": "∞",
      "desc": "Maximum options per question — +Add option disables at this count"
    },
    {
      "name": "disabled",
      "type": "boolean",
      "default": "false",
      "desc": "Disable adding questions"
    },
    {
      "name": "change",
      "type": "CustomEvent<{questions}>",
      "default": "—",
      "desc": "Composed event on any edit; read e.detail.questions (the full model)"
    }
  ],
  "tokens": [
    "color-primary",
    "border-hover",
    "border-disabled",
    "bg-container",
    "bg-container-disabled",
    "bg-accent",
    "text-disabled",
    "radius-default"
  ],
  "spec": [
    {
      "label": "Question",
      "value": "a collapsible NumberedItem — prompt (counted input) + OptionRow choices + \"+ Add option\""
    },
    {
      "label": "Add question",
      "value": "full-width dashed \"+ Add question\" (system-plus), disabled at max (SETTINGS-31)"
    },
    {
      "label": "Add option",
      "value": "per-question \"+ Add option\", disabled at maxoptions"
    },
    {
      "label": "Limits",
      "value": "no \"X of Y\" counter — +Add disables at max, delete disables at min (SETTINGS-36)"
    },
    {
      "label": "Delete",
      "value": "question delete disables at min; option delete disables at minoptions"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Question list",
        "when": "an editable set of questions, each with its own prompt + choices (a quiz builder, a survey)"
      },
      {
        "what": "Option row list",
        "when": "a flat list of options for ONE question — compose OptionRow directly"
      }
    ],
    "note": "The primary +Add spans the full panel width (SETTINGS-31) — never hugged to one side. Never a textual \"X of Y\" count (SETTINGS-36) — the +Add disables at max and delete disables at min. Composes NumberedItem + OptionRow; it does not re-implement the numbered card or the option row.",
    "dsv3": "COVERED — the settings-lab QuestionList control mapped into the DS. It composes NumberedItem and OptionRow into the editable-questions surface (SETTINGS-31/36), reusing the DS composites rather than a hand-rolled numbered box."
  },
  "surfaces": [
    "editor",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-question-list> is the SAME shared custom element React and Vue consume — here in its native\n     form. It COMPOSES DS composites — NumberedItem (each question card) + OptionRow (each choice).\n     Theming comes only from the --aha-* tokens in tokens.css. Collapse a question with its caret. -->\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-question-list.js';   // registers <aha-question-list>\n</script>\n\n<div style=\"max-width:440px\">\n  <aha-question-list id=\"quiz\" min=\"1\" max=\"10\" minoptions=\"2\" maxoptions=\"6\"></aha-question-list>\n</div>\n\n<script type=\"module\">\n  const el = document.getElementById('quiz');\n  el.questions = [\n    { id: 'q1', prompt: 'Capital of France?', options: [\n      { id: 'o1', text: 'Paris', correct: true }, { id: 'o2', text: 'London' } ] },\n    { id: 'q2', prompt: 'Largest ocean?', options: [\n      { id: 'o3', text: 'Pacific', correct: true }, { id: 'o4', text: 'Atlantic' } ] },\n  ];\n  el.addEventListener('change', (e) => console.log('questions:', e.detail.questions));\n</script>\n",
    "react": "import '@ahaslides-product/design/aha-question-list'; // registers <aha-question-list>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper; React 19 can use <aha-question-list> directly.\n// questions is a PROPERTY (the model); onChange gives the full edited model back.\nfunction AhaQuestionList({ questions, min, max, minOptions, maxOptions, disabled, onChange }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    el.questions = questions || [];\n    const h = (e) => onChange?.(e.detail.questions);   // composed CustomEvent\n    el.addEventListener('change', h);\n    return () => el.removeEventListener('change', h);\n  }, [questions, onChange]);\n  return (\n    <aha-question-list\n      ref={ref}\n      {...(min != null ? { min: String(min) } : {})}\n      {...(max != null ? { max: String(max) } : {})}\n      {...(minOptions != null ? { minoptions: String(minOptions) } : {})}\n      {...(maxOptions != null ? { maxoptions: String(maxOptions) } : {})}\n      {...(disabled ? { disabled: '' } : {})}\n    />\n  );\n}\n\n// usage\n<AhaQuestionList questions={quiz} min={1} max={10} minOptions={2} maxOptions={6} onChange={setQuiz} />\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-question-list';   // registers <aha-question-list>\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst quiz = ref([\n  { id: 'q1', prompt: 'Capital of France?', options: [{ id: 'o1', text: 'Paris', correct: true }, { id: 'o2', text: 'London' }] },\n]);\n</script>\n\n<template>\n  <!-- questions bound as a PROPERTY; @change returns the full edited model -->\n  <aha-question-list\n    min=\"1\"\n    max=\"10\"\n    minoptions=\"2\"\n    maxoptions=\"6\"\n    :questions.prop=\"quiz\"\n    @change=\"quiz = $event.detail.questions\"\n  />\n</template>\n"
  }
}
