{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "toggle-group",
  "name": "Toggle group",
  "category": "Actions",
  "description": "A set of pressable buttons, single choice like a segmented control, or multiple like a formatting toolbar.",
  "tags": [
    "toggle",
    "segmented control",
    "toolbar",
    "pressed",
    "radio group"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "segmented control",
      "view switcher",
      "formatting toolbar",
      "choose one option inline",
      "grid list toggle"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [
      "tabs",
      "radio-card"
    ],
    "avoid": null
  },
  "url": "https://mian.ro/components/toggle-group",
  "markdownUrl": "https://mian.ro/ai/components/toggle-group.md",
  "classes": [
    {
      "name": "mn-toggle-group",
      "description": "Container."
    },
    {
      "name": "data-mn-toggle=\"single | multiple\"",
      "description": "Button mode, handled by mian.js."
    },
    {
      "name": "mn-toggle-group-block",
      "description": "Stretches to full width."
    }
  ],
  "accessibility": [
    "Button mode sets aria-pressed on each button; arrow keys move between them.",
    "For a form value with no JavaScript, use the label + radio version: it submits with the form and is announced as a radio group."
  ],
  "examples": [
    {
      "title": "Single choice",
      "description": null,
      "html": "<div class=\"mn-toggle-group\" data-mn-toggle=\"single\" aria-label=\"View\">\n  <button aria-pressed=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"4\" y=\"4\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"13\" y=\"4\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"4\" y=\"13\" width=\"7\" height=\"7\" rx=\"1\"/><rect x=\"13\" y=\"13\" width=\"7\" height=\"7\" rx=\"1\"/></svg> Grid</button>\n  <button><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M9 6h11M9 12h11M9 18h11M4 6h.01M4 12h.01M4 18h.01\"/></svg> List</button>\n  <button><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M4 20V10M10 20V4M16 20v-7M22 20H2\"/></svg> Chart</button>\n</div>"
    },
    {
      "title": "Multiple",
      "description": null,
      "html": "<div class=\"mn-toggle-group\" data-mn-toggle=\"multiple\" aria-label=\"Text formatting\">\n  <button aria-label=\"Bold\" aria-pressed=\"true\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7 5h6a3.5 3.5 0 0 1 0 7H7zM7 12h7a3.5 3.5 0 0 1 0 7H7z\"/></svg></button>\n  <button aria-label=\"Italic\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M15 5h-5M14 19H9M14 5l-4 14\"/></svg></button>\n  <button aria-label=\"Underline\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7 4v7a5 5 0 0 0 10 0V4M5 20h14\"/></svg></button>\n</div>"
    },
    {
      "title": "As a form field (no JS)",
      "description": null,
      "html": "<div class=\"mn-toggle-group mn-toggle-group-block\" role=\"radiogroup\" aria-label=\"Billing period\">\n  <label><input type=\"radio\" name=\"period\" value=\"monthly\" checked> Monthly</label>\n  <label><input type=\"radio\" name=\"period\" value=\"yearly\"> Yearly</label>\n  <label><input type=\"radio\" name=\"period\" value=\"lifetime\"> Lifetime</label>\n</div>"
    }
  ],
  "css": ".mn-toggle-group {\n  display: inline-flex; gap: 2px; padding: 3px;\n  border-radius: calc(var(--mn-radius-field) + 3px);\n  background: var(--mn-surface-2);\n}\n.mn-toggle-group > button, .mn-toggle-group > label {\n  position: relative;\n  display: inline-flex; align-items: center; justify-content: center; gap: .45em;\n  height: calc(var(--mn-size-md) - 6px); padding: 0 .85em;\n  border: 0; border-radius: var(--mn-radius-field);\n  background: transparent; color: var(--mn-muted);\n  font: 500 var(--mn-text-sm)/1 var(--mn-font); white-space: nowrap;\n  cursor: pointer;\n  transition: background-color var(--mn-duration) var(--mn-ease), color var(--mn-duration) var(--mn-ease), box-shadow var(--mn-duration) var(--mn-ease);\n}\n.mn-toggle-group > :hover { color: var(--mn-fg); }\n.mn-toggle-group > [aria-pressed=\"true\"], .mn-toggle-group > [aria-checked=\"true\"], .mn-toggle-group > label:has(input:checked) {\n  background: var(--mn-surface); color: var(--mn-fg); box-shadow: 0 1px 2px rgb(0 0 0 / .08), 0 0 0 1px var(--mn-border);\n}\n.mn-toggle-group > label > input { position: absolute; opacity: 0; pointer-events: none; }\n.mn-toggle-group > label:has(input:focus-visible), .mn-toggle-group > button:focus-visible { outline: none; box-shadow: var(--mn-focus); }\n.mn-toggle-group svg { width: 1.1em; height: 1.1em; }\n.mn-toggle-group-block { display: flex; }\n.mn-toggle-group-block > * { flex: 1; }",
  "js": "register('toggle-group', (root) => {\n  each(root, '.mn-toggle-group[data-mn-toggle]', 'Tgl', (g) => {\n    const single = g.dataset.mnToggle !== 'multiple';\n    const btns = () => [...g.querySelectorAll(':scope > button')];\n    if (single) g.setAttribute('role', 'group');\n    btns().forEach((b) => { if (!b.hasAttribute('aria-pressed')) b.setAttribute('aria-pressed', 'false'); });\n    g.addEventListener('click', (e) => {\n      const b = e.target.closest('button');\n      if (!b || !g.contains(b) || b.disabled) return;\n      if (single) btns().forEach((x) => x.setAttribute('aria-pressed', String(x === b)));\n      else b.setAttribute('aria-pressed', String(b.getAttribute('aria-pressed') !== 'true'));\n      emit(g, 'change', { value: btns().filter((x) => x.getAttribute('aria-pressed') === 'true').map((x) => x.value || x.textContent.trim()) });\n    });\n    g.addEventListener('keydown', (e) => {\n      const list = btns().filter((b) => !b.disabled);\n      const i = list.indexOf(document.activeElement);\n      if (i < 0) return;\n      let n = -1;\n      if (e.key === 'ArrowRight' || e.key === 'ArrowDown') n = (i + 1) % list.length;\n      else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') n = (i - 1 + list.length) % list.length;\n      if (n >= 0) { e.preventDefault(); list[n].focus(); }\n    });\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/toggle-group.json"
  }
}
