{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "check-group",
  "type": "registry:block",
  "title": "Check Group",
  "description": "Checkbox and radio sets as card rows on real native inputs: boxes squash and spring back with a pop, ticks draw on with a slight overshoot, a select-all parent morphs between a dash and a tick, and a highlight glides on a spring to the chosen radio.",
  "docs": "Check Group was added to public/vividsites/check-group.html. Paste its markup, <style> and <script> where you want it on the page (or open the file to see it alone). Customise it with the CSS variables at the top. Docs and live demo: https://vividsites.app/c/check-group",
  "categories": [
    "ui",
    "forms"
  ],
  "files": [
    {
      "path": "components/vividsites/check-group.html",
      "type": "registry:file",
      "target": "public/vividsites/check-group.html",
      "content": "<!--\nvs-check-group: checkbox and radio sets as card rows, built on real native inputs (forms,\n  labels, Space, arrow keys and screen readers work as the browser intends). The box\n  squashes under your finger and springs back with a pop when it checks; the tick draws on\n  and settles with a slight overshoot; a \"select all\" parent draws a dash when only some\n  children are on, morphing to a tick when all are. In a radio set the dot springs in and a\n  highlight glides on a spring to the chosen row.\n\n  Markup: a <fieldset class=\"vs-check-group\"> with an optional <legend class=\"vs-check-group-legend\">\n  and one <label class=\"vs-check-group-row\"> per option:\n    <label class=\"vs-check-group-row\">\n      <input class=\"vs-check-group-input\" type=\"checkbox\" name=\"email\" value=\"replies\" checked>\n      <span class=\"vs-check-group-mark\"></span>                     (the script draws the glyph in it)\n      <span class=\"vs-check-group-text\">\n        <span class=\"vs-check-group-label\">Replies and mentions</span>\n        <span class=\"vs-check-group-desc\">Optional smaller line</span>\n      </span>\n      <span class=\"vs-check-group-tag\">Required</span>            (optional)\n    </label>\n  Radios: same rows with type=\"radio\" and one shared name; arrows move the selection (native).\n  Select all: give one checkbox the data-vs-all attribute; every other checkbox in the fieldset\n    becomes its child (disabled children keep their state). An element with\n    data-vs-count inside that row shows \"3 of 4\".\n  Variables (on .vs-check-group): --vs-check-group-size (scales everything), -accent, -on-accent,\n    -panel, -ink, -mute, -ring, -radius (panel), -row-radius, -box-radius (checkbox corner),\n    -width, -font, -bounce (0 = no overshoot, 1 = very springy).\n  Events: the inputs fire their normal \"change\"; the fieldset also fires \"vs-check-change\"\n    with detail {values: [...checked values]}.\n  Reduced motion: no squash, pop or glide; ticks still draw quickly.\n-->\n<style>\n.vs-check-group {\n  --vs-check-group-size: 16px;\n  --vs-check-group-accent: #ff5a36;\n  --vs-check-group-on-accent: #ffffff;\n  --vs-check-group-panel: #fffaf7;\n  --vs-check-group-ink: #2b1611;\n  --vs-check-group-mute: color-mix(in srgb, var(--vs-check-group-ink) 55%, transparent);\n  --vs-check-group-ring: color-mix(in srgb, var(--vs-check-group-ink) 22%, transparent);\n  --vs-check-group-radius: 1.5em;\n  --vs-check-group-row-radius: 1em;\n  --vs-check-group-box-radius: .42em;\n  --vs-check-group-width: 25em;\n  --vs-check-group-font: inherit;\n  --vs-check-group-bounce: .6;\n\n  --vs-check-group-pop: linear(0, .046, .165, .328, .509, .688, .85, .985, 1.087, 1.156, 1.194, 1.205, 1.196, 1.171, 1.138, 1.101, 1.064, 1.031, 1.003, .982, .968, .96, .958, .96, .965, .972, .979, .987, .994, .999, 1);\n  position: relative; box-sizing: border-box; min-width: 0; width: min(100%, var(--vs-check-group-width));\n  margin: 0; padding: .5em; border: 0;\n  font-size: var(--vs-check-group-size); font-family: var(--vs-check-group-font); line-height: 1.35;\n  color: var(--vs-check-group-ink); background: var(--vs-check-group-panel); border-radius: var(--vs-check-group-radius);\n  box-shadow: 0 0 0 .5px color-mix(in srgb, var(--vs-check-group-ink) 10%, transparent), inset 0 1px 0 #fff,\n              0 1px 2px rgba(80,30,10,.05), 0 .8em 2em -1em rgba(120,40,10,.18), 0 2.5em 4em -2em rgba(120,40,10,.16);\n  -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent;\n}\n.vs-check-group *, .vs-check-group *::before, .vs-check-group *::after { box-sizing: border-box; }\n.vs-check-group-legend {\n  float: left; width: 100%; padding: .85em 1.15em .55em; margin: 0;\n  font-size: .7em; font-weight: 500; font-stretch: 125%; letter-spacing: .12em; text-transform: uppercase; color: var(--vs-check-group-mute);\n}\n.vs-check-group-legend + * { clear: both; }\n\n/* rows */\n.vs-check-group-row {\n  position: relative; z-index: 1; display: grid; grid-template-columns: auto 1fr auto; align-items: start; column-gap: .95em;\n  padding: .95em 1.1em; border-radius: var(--vs-check-group-row-radius); cursor: pointer; user-select: none; -webkit-user-select: none;\n  transition: background-color .2s cubic-bezier(.2,0,0,1), box-shadow .2s;\n}\n.vs-check-group-row + .vs-check-group-row { margin-top: .125em; }\n.vs-check-group-row:hover { background: color-mix(in srgb, var(--vs-check-group-accent) 6%, transparent); }\n/* checked checkbox rows keep a faint tint of the accent */\n.vs-check-group-row:not(.is-all):has(.vs-check-group-input[type=\"checkbox\"]:checked:not(:disabled)) { background: color-mix(in srgb, var(--vs-check-group-accent) 4.5%, transparent); }\n.vs-check-group-row:not(.is-all):has(.vs-check-group-input[type=\"checkbox\"]:checked:not(:disabled)):hover { background: color-mix(in srgb, var(--vs-check-group-accent) 8%, transparent); }\n.vs-check-group-row:has(.vs-check-group-input:focus-visible) {\n  box-shadow: inset 0 0 0 1.5px var(--vs-check-group-accent), 0 0 0 4px color-mix(in srgb, var(--vs-check-group-accent) 20%, transparent);\n}\n.vs-check-group-row.is-all { margin-bottom: .55em; }\n.vs-check-group-row.is-all::after {\n  content: \"\"; position: absolute; left: 1.1em; right: 1.1em; bottom: -.33em; height: 1px; pointer-events: none;\n  background: color-mix(in srgb, var(--vs-check-group-ink) 9%, transparent); transform: scaleY(.5);\n}\n.vs-check-group-input { position: absolute; left: 1.1em; top: .95em; width: 1.375em; height: 1.375em; margin: 0; opacity: 0; pointer-events: none; }\n.vs-check-group-text { display: grid; gap: .18em; min-width: 0; padding-top: .06em; }\n.vs-check-group-label { font-weight: 500; font-stretch: 112.5%; letter-spacing: -.005em; }\n.vs-check-group-desc { font-size: .84em; color: var(--vs-check-group-mute); line-height: 1.4; }\n.vs-check-group-tag, [data-vs-count] {\n  align-self: center; font-size: .72em; font-weight: 500; font-stretch: 112.5%; letter-spacing: .02em; white-space: nowrap;\n  padding: .3em .7em; border-radius: 999px; color: var(--vs-check-group-mute);\n  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vs-check-group-ink) 12%, transparent);\n}\n[data-vs-count] { font-variant-numeric: tabular-nums; box-shadow: none; background: color-mix(in srgb, var(--vs-check-group-accent) 10%, transparent); color: color-mix(in srgb, var(--vs-check-group-accent) 75%, var(--vs-check-group-ink)); }\n\n/* the box / circle */\n.vs-check-group-mark {\n  position: relative; display: block; width: 1.375em; height: 1.375em; margin-top: .02em; flex: none;\n  border-radius: var(--vs-check-group-box-radius); background: #fff;\n  box-shadow: inset 0 0 0 1.5px var(--vs-check-group-ring), inset 0 1.5px 2px rgba(60,20,10,.06), 0 1px 0 rgba(255,255,255,.9);\n  transition: box-shadow .2s; will-change: transform;\n}\n.vs-check-group-row:hover .vs-check-group-mark { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--vs-check-group-accent) 55%, var(--vs-check-group-ring)), inset 0 1.5px 2px rgba(60,20,10,.06), 0 1px 0 rgba(255,255,255,.9); }\n.vs-check-group-mark::before {   /* the accent fill grows from the centre */\n  content: \"\"; position: absolute; inset: 0; border-radius: inherit; background: var(--vs-check-group-accent);\n  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), inset 0 -1px 1px rgba(0,0,0,.08),\n              0 1px 2px color-mix(in srgb, var(--vs-check-group-accent) 50%, transparent), 0 .35em .8em -.25em color-mix(in srgb, var(--vs-check-group-accent) 80%, transparent);\n  transform: scale(.35); opacity: 0;\n  transition: transform .22s cubic-bezier(.4,0,.6,1) var(--d, 0s), opacity .16s cubic-bezier(.4,0,.6,1) var(--d, 0s);\n}\n.vs-check-group-input:checked + .vs-check-group-mark::before,\n.vs-check-group-input:indeterminate + .vs-check-group-mark::before {\n  transform: scale(1); opacity: 1; transition: transform .5s var(--vs-check-group-pop) var(--d, 0s), opacity .12s var(--d, 0s);\n}\n.vs-check-group-mark svg {\n  position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible;\n  fill: none; stroke: var(--vs-check-group-on-accent); stroke-width: 2.7; stroke-linecap: round; stroke-linejoin: round;\n  transform: scale(.72); transition: transform .2s cubic-bezier(.4,0,.6,1) var(--d, 0s);\n}\n.vs-check-group-input:checked + .vs-check-group-mark svg,\n.vs-check-group-input:indeterminate + .vs-check-group-mark svg { transform: scale(1); transition: transform .6s var(--vs-check-group-pop) calc(.06s + var(--d, 0s)); }\n.vs-check-group-tick, .vs-check-group-dash {\n  stroke-dasharray: 1 2; stroke-dashoffset: 1; opacity: 0;\n  transition: stroke-dashoffset .14s cubic-bezier(.4,0,1,1) var(--d, 0s), opacity 0s calc(.14s + var(--d, 0s));\n}\n.vs-check-group-input:checked:not(:indeterminate) + .vs-check-group-mark .vs-check-group-tick,\n.vs-check-group-input:indeterminate + .vs-check-group-mark .vs-check-group-dash {\n  stroke-dashoffset: 0; opacity: 1;\n  transition: stroke-dashoffset .34s cubic-bezier(.55,0,.2,1) calc(.08s + var(--d, 0s)), opacity 0s calc(.08s + var(--d, 0s));\n}\n/* radio: circle, fill, then the dot springs in */\n.vs-check-group-input[type=\"radio\"] + .vs-check-group-mark { border-radius: 50%; }\n.vs-check-group-input[type=\"radio\"] + .vs-check-group-mark::after {\n  content: \"\"; position: absolute; left: 50%; top: 50%; width: 38%; height: 38%; margin: -19% 0 0 -19%; border-radius: 50%;\n  background: var(--vs-check-group-on-accent); box-shadow: 0 1px 2px rgba(0,0,0,.18);\n  transform: scale(0); transition: transform .16s cubic-bezier(.4,0,1,1);\n}\n.vs-check-group-input[type=\"radio\"]:checked + .vs-check-group-mark::after { transform: scale(1); transition: transform .55s var(--vs-check-group-pop) .07s; }\n\n/* the gliding highlight behind the chosen radio row */\n.vs-check-group-glide {\n  position: absolute; z-index: 0; left: .5em; right: .5em; top: 0; height: 0; border-radius: var(--vs-check-group-row-radius); pointer-events: none;\n  background: color-mix(in srgb, var(--vs-check-group-accent) 9%, var(--vs-check-group-panel));\n  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vs-check-group-accent) 32%, transparent), 0 1px 2px color-mix(in srgb, var(--vs-check-group-accent) 12%, transparent);\n  opacity: 0; transition: opacity .2s; will-change: transform;\n}\n.vs-check-group-glide.is-on { opacity: 1; }\n.vs-check-group:has(.vs-check-group-glide.is-on) .vs-check-group-row:has(input:checked) { background: transparent; }\n\n/* disabled */\n.vs-check-group-row:has(.vs-check-group-input:disabled) { cursor: not-allowed; background: none; }\n.vs-check-group-row:has(.vs-check-group-input:disabled) :is(.vs-check-group-mark, .vs-check-group-text) { opacity: .45; }\n.vs-check-group-row:has(.vs-check-group-input:disabled) .vs-check-group-mark { box-shadow: inset 0 0 0 1.5px var(--vs-check-group-ring); }\n.vs-check-group-input:disabled + .vs-check-group-mark::before { filter: saturate(.35); }\n\n@media (prefers-reduced-motion: reduce) {\n  .vs-check-group *, .vs-check-group *::before, .vs-check-group *::after { transition-duration: .01ms !important; transition-delay: 0s !important; }\n}\n@media (forced-colors: active) {\n  .vs-check-group-mark { border: 1px solid ButtonText; }\n  .vs-check-group-mark::before { background: Highlight; }\n  .vs-check-group-mark svg { stroke: HighlightText; }\n}\n</style>\n\n<fieldset class=\"vs-check-group\">\n  <legend class=\"vs-check-group-legend\">Email me about</legend>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"checkbox\" data-vs-all>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Everything</span></span>\n    <span data-vs-count></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"checkbox\" name=\"email\" value=\"replies\" checked>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Replies and mentions</span><span class=\"vs-check-group-desc\">When someone answers your thread or tags you</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"checkbox\" name=\"email\" value=\"digest\">\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Weekly digest</span><span class=\"vs-check-group-desc\">The five best threads, Sundays at 8 am</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"checkbox\" name=\"email\" value=\"product\" checked>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Product updates</span><span class=\"vs-check-group-desc\">New features, at most twice a month</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"checkbox\" name=\"email\" value=\"billing\" checked disabled>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Billing receipts</span><span class=\"vs-check-group-desc\">Always sent to workspace owners</span></span>\n    <span class=\"vs-check-group-tag\">Required</span>\n  </label>\n</fieldset>\n\n<fieldset class=\"vs-check-group\">\n  <legend class=\"vs-check-group-legend\">Deliver them</legend>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"radio\" name=\"cadence\" value=\"live\">\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">As they happen</span><span class=\"vs-check-group-desc\">One email for each notification</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"radio\" name=\"cadence\" value=\"hourly\" checked>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Hourly bundle</span><span class=\"vs-check-group-desc\">Everything from the last hour, in one email</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"radio\" name=\"cadence\" value=\"morning\">\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Morning roundup</span><span class=\"vs-check-group-desc\">A single email at 7:30 am, your time</span></span>\n  </label>\n  <label class=\"vs-check-group-row\">\n    <input class=\"vs-check-group-input\" type=\"radio\" name=\"cadence\" value=\"weekend\" disabled>\n    <span class=\"vs-check-group-mark\"></span>\n    <span class=\"vs-check-group-text\"><span class=\"vs-check-group-label\">Weekends only</span><span class=\"vs-check-group-desc\">Saturday and Sunday mornings</span></span>\n    <span class=\"vs-check-group-tag\">Studio plan</span>\n  </label>\n</fieldset>\n\n<script>\n(() => {\n  if (window.vsCheckGroup) return;\n  const reduce = matchMedia('(prefers-reduced-motion: reduce)');\n  const live = new Set(); let raf = 0, last = 0;\n  const tick = (now) => {\n    const dt = Math.min(.064, (now - last) / 1000 || .016); last = now;\n    for (const s of live) if (!s.update(dt)) live.delete(s);\n    raf = live.size ? requestAnimationFrame(tick) : 0;\n  };\n  const wake = (s) => { live.add(s); if (!raf) { last = performance.now(); raf = requestAnimationFrame(tick); } };\n  // semi-implicit Euler, sub-stepped at 240 Hz\n  const integrate = (s, dt, k, c) => { const n = Math.max(1, Math.round(dt * 240)), h = dt / n; for (let i = 0; i < n; i++) { s.v += (-k * (s.x - s.g) - c * s.v) * h; s.x += s.v * h; } };\n  const rest = (s, e = .0006) => Math.abs(s.v) < e && Math.abs(s.x - s.g) < e;\n  const GLYPH = '<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\"><path class=\"vs-check-group-tick\" pathLength=\"1\" d=\"M6.4 12.7l3.8 3.8 7.6-8.4\"/><path class=\"vs-check-group-dash\" pathLength=\"1\" d=\"M7.2 12h9.6\"/></svg>';\n  let uid = 0;\n\n  function mount(root) {\n    if (root.__vsCg) return root.__vsCg;\n    const bounce = () => { const b = parseFloat(getComputedStyle(root).getPropertyValue('--vs-check-group-bounce')); return isNaN(b) ? .6 : Math.max(0, Math.min(1, b)); };\n    const rows = [...root.querySelectorAll('.vs-check-group-row')];\n    const items = rows.map((row) => {\n      const input = row.querySelector('input'); if (!input) return null;\n      let mark = row.querySelector('.vs-check-group-mark');\n      if (!mark) { mark = document.createElement('span'); mark.className = 'vs-check-group-mark'; input.after(mark); }\n      mark.setAttribute('aria-hidden', 'true');\n      if (input.type === 'checkbox' && !mark.firstChild) mark.innerHTML = GLYPH;\n      const desc = row.querySelector('.vs-check-group-desc');\n      if (desc) { desc.id ||= `vscg${++uid}`; desc.setAttribute('aria-hidden', 'true'); input.setAttribute('aria-describedby', desc.id); }\n      if (input.hasAttribute('data-vs-all')) row.classList.add('is-all');\n      // squash spring: x < 1 squashes (wider, shorter), x > 1 stretches\n      const it = { row, input, mark, s: { x: 1, v: 0, g: 1 },\n        update(dt) {\n          if (reduce.matches) { this.s.x = 1; this.s.v = 0; mark.style.transform = ''; return false; }\n          const b = bounce(), k = 700, c = 2 * Math.sqrt(k) * (1 - b * .72);\n          integrate(this.s, dt, k, c);\n          const done = rest(this.s) && this.s.g === 1;\n          if (done) { this.s.x = 1; this.s.v = 0; mark.style.transform = ''; return false; }\n          const d = this.s.x - 1;\n          mark.style.transform = `scale(${(1 - d * .5).toFixed(4)},${(1 + d).toFixed(4)})`;\n          return true;\n        } };\n      const press = (on) => { if (input.disabled) return; it.s.g = on ? .8 : 1; wake(it); };\n      row.addEventListener('pointerdown', (e) => { if (e.button === 0) press(true); });\n      row.addEventListener('pointerup', () => press(false));\n      row.addEventListener('pointerleave', () => { if (it.s.g !== 1) press(false); });\n      row.addEventListener('pointercancel', () => press(false));\n      input.addEventListener('keydown', (e) => { if (e.key === ' ' && !e.repeat) press(true); });\n      input.addEventListener('keyup', (e) => { if (e.key === ' ') press(false); });\n      input.addEventListener('blur', () => press(false));\n      return it;\n    }).filter(Boolean);\n    const pop = (it, on) => { if (reduce.matches) return; it.s.g = 1; it.s.v += on ? 5.5 : -2.2; wake(it); };\n\n    // ---- checkboxes + select all ----\n    const all = items.find((it) => it.input.type === 'checkbox' && it.input.hasAttribute('data-vs-all'));\n    const kids = items.filter((it) => it.input.type === 'checkbox' && it !== all);\n    const count = all && all.row.querySelector('[data-vs-count]');\n    const syncAll = () => {\n      if (!all) return;\n      const n = kids.filter((k) => k.input.checked).length;\n      all.input.checked = n === kids.length && n > 0;\n      all.input.indeterminate = n > 0 && n < kids.length;\n      if (count) count.textContent = `${n} of ${kids.length}`;\n    };\n    const emit = () => root.dispatchEvent(new CustomEvent('vs-check-change', { bubbles: true, detail: { values: items.filter((it) => it.input.checked && it.input.value && it !== all).map((it) => it.input.value) } }));\n\n    for (const it of items) {\n      it.input.addEventListener('change', () => {\n        if (it === all) {\n          const free = kids.filter((k) => !k.input.disabled);\n          let target = all.input.checked;\n          if (free.every((k) => k.input.checked === target)) target = !target;  // nothing would change: flip\n          let i = 0;\n          for (const k of free) {\n            if (k.input.checked === target) continue;\n            const d = i++ * 45;\n            // --d staggers the CSS fill and draw, so the change cascades down the list\n            if (!reduce.matches) k.mark.style.setProperty('--d', d + 'ms');\n            k.input.checked = target;\n            setTimeout(() => pop(k, target), d);\n            clearTimeout(k.t); k.t = setTimeout(() => k.mark.style.removeProperty('--d'), d + 700);\n          }\n          syncAll(); pop(all, all.input.checked || all.input.indeterminate);\n        } else if (it.input.type === 'checkbox') {\n          pop(it, it.input.checked); const was = all && (all.input.checked ? 2 : all.input.indeterminate ? 1 : 0);\n          syncAll();\n          if (all && was !== (all.input.checked ? 2 : all.input.indeterminate ? 1 : 0)) pop(all, true);\n        } else {\n          pop(it, true); glideTo(true);\n        }\n        emit();\n      });\n    }\n    syncAll();\n\n    // ---- radio glide ----\n    const radios = items.filter((it) => it.input.type === 'radio');\n    let glide = null;\n    const G = { y: { x: 0, v: 0, g: 0 }, h: { x: 0, v: 0, g: 0 },\n      update(dt) {\n        const b = bounce(), k = 420, c = 2 * Math.sqrt(k) * (1 - b * .45);\n        integrate(this.y, dt, k, c); integrate(this.h, dt, k, c * 1.1);\n        const done = rest(this.y, .05) && rest(this.h, .05);\n        if (done) { this.y.x = this.y.g; this.h.x = this.h.g; this.y.v = this.h.v = 0; }\n        glide.style.transform = `translate3d(0,${this.y.x.toFixed(2)}px,0)`; glide.style.height = this.h.x.toFixed(2) + 'px';\n        return !done;\n      } };\n    const glideTo = (animate) => {\n      if (!glide) return;\n      const on = radios.find((it) => it.input.checked);\n      glide.classList.toggle('is-on', !!on);\n      if (!on) return;\n      G.y.g = on.row.offsetTop; G.h.g = on.row.offsetHeight;\n      if (!animate || reduce.matches || !glide.classList.contains('was-on')) { G.y.x = G.y.g; G.h.x = G.h.g; G.y.v = G.h.v = 0; }\n      glide.classList.add('was-on');\n      wake(G);\n    };\n    if (radios.length) {\n      glide = document.createElement('span'); glide.className = 'vs-check-group-glide'; glide.setAttribute('aria-hidden', 'true');\n      root.prepend(glide);\n      if (!root.hasAttribute('role') && root.tagName !== 'FIELDSET') root.setAttribute('role', 'radiogroup');\n      new ResizeObserver(() => glideTo(false)).observe(root);\n      glideTo(false);\n      // form reset restores defaults: re-sync after the browser has reset the inputs\n      const form = root.closest('form'); if (form) form.addEventListener('reset', () => setTimeout(() => { syncAll(); glideTo(true); }));\n    } else {\n      const form = root.closest('form'); if (form) form.addEventListener('reset', () => setTimeout(syncAll));\n    }\n\n    const api = { sync: () => { syncAll(); glideTo(true); } };\n    root.vsSync = api.sync;\n    root.__vsCg = api;\n    return api;\n  }\n  const init = () => document.querySelectorAll('.vs-check-group').forEach(mount);\n  document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init();\n  window.vsCheckGroup = { mount };\n})();\n</script>\n"
    }
  ],
  "meta": {
    "category": "ui",
    "group": "Forms",
    "tags": [
      "checkbox",
      "radio",
      "select all",
      "indeterminate",
      "form",
      "spring",
      "settings",
      "accessibility"
    ],
    "tech": [
      "CSS",
      "JS"
    ],
    "deps": [
      "https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@100..125,400..500&display=swap"
    ],
    "source": null,
    "free": true,
    "accent": "#ff5a36",
    "added": "2026-10-01",
    "demo": "https://vividsites.app/api/component/check-group/index.html",
    "thumbnail": "https://vividsites.app/api/component/check-group/thumb.jpg",
    "video": "https://vividsites.app/api/component/check-group/thumb.mp4",
    "page": "https://vividsites.app/c/check-group"
  }
}