{
  "generatedFrom": "mode-field.contract.json",
  "component": "Mode field",
  "slug": "mode-field",
  "group": "Settings",
  "tier": "leaf-lit",
  "summary": "A labelled field with an inline exclusive mode control whose body swaps in place — the settings field for a control that has two-or-more mutually-exclusive modes.",
  "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-mode-field';",
    "element": "aha-mode-field",
    "registers": "aha-mode-field"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/mode-field/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/mode-field/mode-field.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/mode-field.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/mode-field.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-mode-field> 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-mode-field"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-mode-field"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-mode-field",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "label",
      "type": "string",
      "default": "—",
      "desc": "The field label, left of the mode control"
    },
    {
      "name": "options",
      "type": "Array<{value,label,icon?}>",
      "default": "[]",
      "desc": "The exclusive modes; set as a property or a JSON attribute (alias: modes)"
    },
    {
      "name": "value",
      "type": "string",
      "default": "—",
      "desc": "The active option's value (alias: mode)"
    },
    {
      "name": "variant",
      "type": "'radio' | 'segmented'",
      "default": "'radio'",
      "desc": "radio = outline segmented (brand border, white fill); segmented = neutral grey"
    },
    {
      "name": "labelVariant",
      "type": "'field' | 'section'",
      "default": "'field'",
      "desc": "field = regular weight; section = semibold header for a grouped-list body (attr: label-variant)"
    },
    {
      "name": "help",
      "type": "string",
      "default": "—",
      "desc": "When set, a \"?\" tooltip after the label (icon system-question-mark)"
    },
    {
      "name": "disabled",
      "type": "boolean",
      "default": "false",
      "desc": "Non-interactive; grey buttons"
    },
    {
      "name": "change",
      "type": "CustomEvent<{value}>",
      "default": "—",
      "desc": "Composed event on change; read e.detail.value (e.detail.mode is a back-compat alias)"
    }
  ],
  "tokens": [
    "border-strong",
    "border-hover",
    "border-disabled",
    "color-primary",
    "bg-container",
    "bg-container-disabled",
    "gray-20",
    "gray-100",
    "text-default",
    "text-secondary",
    "text-tertiary",
    "text-disabled"
  ],
  "spec": [
    {
      "label": "Head",
      "value": "label LEFT / mode control RIGHT on one row; gap 12, wraps (row-gap 8) on narrow; 32 min-height"
    },
    {
      "label": "Radio variant (default)",
      "value": "OUTLINE segmented, 32 high, semibold 13; selected = #6A1EBB border + #6A1EBB text on WHITE fill (no tint pill)"
    },
    {
      "label": "Segmented variant",
      "value": "neutral grey control — #F7F7F7 track, secondary-grey labels, active = white raised on default-text"
    },
    {
      "label": "Label variant",
      "value": "field = regular 400; section = semibold 600 (a header for a grouped-list body)"
    },
    {
      "label": "Help",
      "value": "optional \"?\" tooltip (system-question-mark) after the label when `help` is set; opens below (bottom-start) so it clears the panel top, and the bubble wraps within 240px"
    },
    {
      "label": "Body",
      "value": "the active value's light-DOM child; others hidden in place (persistent, keep state); margin-top 12; de-emphasised help text — secondary #4A4A4A, 13/20, regular — so the label stays the field's primary line"
    },
    {
      "label": "Hover",
      "value": "#D3B4FF border + #6A1EBB label on a radio-variant button"
    },
    {
      "label": "Disabled",
      "value": "#F1F1F1 fill, #EBEBEB border, not-allowed"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Mode field",
        "when": "a single setting whose control has 2+ exclusive modes and a mode-specific body (automatic vs manual, value-source A vs B)"
      },
      {
        "what": "Segmented",
        "when": "an exclusive choice with NO mode-specific body to swap"
      },
      {
        "what": "Switch",
        "when": "a plain immediate on/off with no modes"
      }
    ],
    "note": "Only the active value's body is live — the others stay in the DOM (persistent) but hidden, so switching back restores their state. Default `variant='radio'` is an OUTLINE segmented control (brand border + brand text on white); a solid brand-filled pill is NOT the mode-field look — pick `variant='segmented'` for the neutral grey alternative. A mode toggle for an action may keep a verb-led label ('Show results manually') per SETTINGS-01.",
    "dsv3": "COVERED — the settings-lab ModeField control mapped into the DS. The radio-outline + grey-segmented mode controls reuse the DS V3 segmented family; the label + inline-mode head row (with the optional \"?\" help tooltip), the field/section label weight, and the swap-in-place persistent body are the settings-specific shape."
  },
  "surfaces": [
    "editor",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-mode-field> is the SAME shared custom element React and Vue consume — here in its native form.\n     Theming comes only from the --aha-* tokens in tokens.css. The bodies are light-DOM children tagged\n     data-mode; switching the active value toggles which is visible IN PLACE (the hidden one keeps its\n     state). variant=\"radio\" (default) is the OUTLINE mode control; variant=\"segmented\" is the grey one. -->\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-mode-field.js';   // registers <aha-mode-field>\n</script>\n\n<aha-mode-field id=\"results\" label=\"Results\" value=\"auto\"\n  help=\"Choose when the results chart appears to the audience.\" style=\"max-width:360px\">\n  <div data-mode=\"auto\">Results appear automatically when everyone has answered.</div>\n  <div data-mode=\"manual\">You reveal results with the \"Show results\" button.</div>\n</aha-mode-field>\n\n<script type=\"module\">\n  const el = document.getElementById('results');\n  el.options = [{ value: 'auto', label: 'Automatic' }, { value: 'manual', label: 'Manual' }];\n  el.addEventListener('change', (e) => console.log('value:', e.detail.value));\n</script>\n",
    "react": "import '@ahaslides-product/design/aha-mode-field'; // registers <aha-mode-field>\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper; React 19 can use <aha-mode-field> directly.\n// options is a PROPERTY; bodies are children tagged data-mode — the hidden one keeps its state.\n// variant: 'radio' (default outline) | 'segmented' (neutral grey). labelVariant: 'field' | 'section'.\nfunction AhaModeField({ label, options, value, variant, labelVariant, help, disabled, onChange, children }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    el.options = options || [];\n    if (value != null) el.value = value;\n    if (variant != null) el.variant = variant;\n    if (labelVariant != null) el.labelVariant = labelVariant;\n    if (help != null) el.help = help;\n    el.disabled = !!disabled;\n    const h = (e) => onChange?.(e.detail.value);   // composed CustomEvent\n    el.addEventListener('change', h);\n    return () => el.removeEventListener('change', h);\n  }, [options, value, variant, labelVariant, help, disabled, onChange]);\n  return <aha-mode-field ref={ref} label={label}>{children}</aha-mode-field>;\n}\n\n// usage\n<AhaModeField\n  label=\"Results\"\n  value={resultsMode}\n  options={[{ value: 'auto', label: 'Automatic' }, { value: 'manual', label: 'Manual' }]}\n  help=\"Choose when the results chart appears.\"\n  onChange={setResultsMode}\n>\n  <div data-mode=\"auto\">Results appear automatically.</div>\n  <div data-mode=\"manual\">You reveal results manually.</div>\n</AhaModeField>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-mode-field';   // registers <aha-mode-field>\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { ref } from 'vue';\nconst resultsMode = ref('auto');\nconst options = [{ value: 'auto', label: 'Automatic' }, { value: 'manual', label: 'Manual' }];\n</script>\n\n<template>\n  <!-- options + value bound as PROPERTIES; bodies are children tagged data-mode -->\n  <!-- variant=\"radio\" (default outline) | \"segmented\"; label-variant=\"field\" | \"section\" -->\n  <aha-mode-field\n    label=\"Results\"\n    help=\"Choose when the results chart appears.\"\n    :options.prop=\"options\"\n    :value.prop=\"resultsMode\"\n    @change=\"resultsMode = $event.detail.value\"\n  >\n    <div data-mode=\"auto\">Results appear automatically.</div>\n    <div data-mode=\"manual\">You reveal results manually.</div>\n  </aha-mode-field>\n</template>\n"
  }
}
