{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-checkbox-group",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-checkbox-group.tsx",
      "content": "\"use client\";\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\";\nimport { CheckboxGroup as CheckboxGroupPrimitive } from \"@base-ui/react/checkbox-group\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\nconst springTap = {\n  type: \"spring\" as const,\n  stiffness: 520,\n  damping: 36,\n  mass: 0.35,\n};\n\nconst labelTransition = { duration: 0.2 } as const;\nconst instantTransition = { duration: 0 } as const;\nconst pathEase = [0.65, 0, 0.35, 1] as const;\n\nconst checkmarkVariants = {\n  checked: {\n    pathLength: 1,\n    opacity: 1,\n    transition: {\n      pathLength: { duration: 0.3, ease: pathEase },\n      opacity: { duration: 0.05, delay: 0.06 },\n    },\n  },\n  unchecked: {\n    pathLength: 0,\n    opacity: 0,\n    transition: {\n      pathLength: { duration: 0.25, ease: pathEase },\n      opacity: { duration: 0.1, delay: 0.18 },\n    },\n  },\n} as const;\n\nconst reducedCheckmarkVariants = {\n  checked: { pathLength: 1, opacity: 1 },\n  unchecked: { pathLength: 0, opacity: 0 },\n} as const;\n\nexport type CheckboxGroupSize = \"default\" | \"lg\" | \"sm\";\n\nconst sizeStyles: Record<\n  CheckboxGroupSize,\n  {\n    box: string;\n    gap: string;\n    icon: string;\n    label: string;\n    row: string;\n  }\n> = {\n  sm: {\n    box: \"h-4 w-4\",\n    gap: \"gap-2\",\n    icon: \"h-3 w-3\",\n    label: \"text-xs\",\n    row: \"px-3 py-2.5\",\n  },\n  default: {\n    box: \"h-[18px] w-[18px]\",\n    gap: \"gap-3\",\n    icon: \"h-4 w-4\",\n    label: \"text-sm\",\n    row: \"px-4 py-3\",\n  },\n  lg: {\n    box: \"h-5 w-5\",\n    gap: \"gap-3.5\",\n    icon: \"h-4 w-4\",\n    label: \"text-base\",\n    row: \"px-4 py-3.5\",\n  },\n};\n\nexport interface CheckboxGroupOption {\n  description?: React.ReactNode;\n  disabled?: boolean;\n  disabledReason?: string;\n  group?: string;\n  id?: string;\n  label: React.ReactNode;\n  readOnly?: boolean;\n  value: string;\n}\n\nexport interface CheckboxGroupProps {\n  \"aria-describedby\"?: string;\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n  children?: React.ReactNode;\n  className?: string;\n  defaultValue?: string[];\n  disabled?: boolean;\n  form?: string;\n  invalid?: boolean;\n  maxVisible?: number;\n  name?: string;\n  onChange?: (value: string[]) => void;\n  options?: CheckboxGroupOption[];\n  showLessLabel?: string;\n  showMoreLabel?: string;\n  size?: CheckboxGroupSize;\n  value?: string[];\n}\n\nexport interface CheckboxGroupSectionProps {\n  children: React.ReactNode;\n  disabled?: boolean;\n  label?: string;\n}\n\nexport interface CheckboxGroupItemProps {\n  description?: React.ReactNode;\n  disabled?: boolean;\n  disabledReason?: string;\n  id?: string;\n  label: React.ReactNode;\n  readOnly?: boolean;\n  value: string;\n}\n\ninterface ParsedCheckboxGroupSection {\n  disabled: boolean;\n  key: string;\n  label?: string;\n  options: Array<{ index: number; option: CheckboxGroupOption }>;\n}\n\ninterface CheckboxGroupModel {\n  options: CheckboxGroupOption[];\n  sections: ParsedCheckboxGroupSection[];\n}\n\ntype CheckboxRootRenderProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  ref?: React.Ref<HTMLButtonElement>;\n  style?: React.CSSProperties;\n};\n\ntype CheckboxGroupRowProps = {\n  checked: boolean;\n  descriptionId?: string;\n  disabled: boolean;\n  form?: string;\n  groupDisabled: boolean;\n  invalid: boolean;\n  isHidden: boolean;\n  labelId: string;\n  name?: string;\n  option: CheckboxGroupOption;\n  prefersReducedMotion: boolean;\n  sizing: (typeof sizeStyles)[CheckboxGroupSize];\n};\n\nfunction setRef<T>(ref: React.Ref<T> | undefined, value: T) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T>).current = value;\n  }\n}\n\nfunction getGroupKey(option: CheckboxGroupOption) {\n  return option.group?.trim() ?? \"\";\n}\n\nfunction buildSections(\n  entries: Array<{ index: number; option: CheckboxGroupOption }>\n): ParsedCheckboxGroupSection[] {\n  return entries.reduce<ParsedCheckboxGroupSection[]>(\n    (sections, entry, sectionIndex) => {\n      const label = getGroupKey(entry.option);\n      const previousSection = sections.at(-1);\n\n      if (previousSection && previousSection.label === label) {\n        previousSection.options.push(entry);\n        return sections;\n      }\n\n      sections.push({\n        disabled: false,\n        key: label ? `${label}-${sectionIndex}` : `section-${sectionIndex}`,\n        label: label || undefined,\n        options: [entry],\n      });\n\n      return sections;\n    },\n    []\n  );\n}\n\nfunction itemPropsToOption(props: CheckboxGroupItemProps): CheckboxGroupOption {\n  const value = props.value.trim();\n\n  if (!value) {\n    warnInDevelopment(\"CheckboxGroupItem requires a non-empty value.\");\n  }\n\n  return {\n    description: props.description,\n    disabled: props.disabled,\n    disabledReason: props.disabledReason,\n    id: props.id,\n    label: props.label,\n    readOnly: props.readOnly,\n    value,\n  };\n}\n\nfunction flattenCheckboxGroupChildren(children: React.ReactNode) {\n  return React.Children.toArray(children).filter(\n    (child): child is React.ReactElement =>\n      React.isValidElement(child) && child.type !== React.Fragment\n  );\n}\n\nfunction hasCompoundChildren(children: React.ReactNode) {\n  return flattenCheckboxGroupChildren(children).some(\n    (child) =>\n      isCheckboxGroupItemElement(child) || isCheckboxGroupSectionElement(child)\n  );\n}\n\nfunction warnInDevelopment(message: string) {\n  if (process.env.NODE_ENV !== \"production\") {\n    console.warn(`[CheckboxGroup] ${message}`);\n  }\n}\n\nfunction appendItemToModel(\n  model: CheckboxGroupModel,\n  itemProps: CheckboxGroupItemProps\n) {\n  const option = itemPropsToOption(itemProps);\n  const index = model.options.length;\n\n  model.options.push(option);\n\n  return { index, option };\n}\n\nfunction buildModelFromOptions(\n  options: CheckboxGroupOption[]\n): CheckboxGroupModel {\n  const allEntries = options.map((option, index) => ({ index, option }));\n\n  return {\n    options,\n    sections: buildSections(allEntries),\n  };\n}\n\nfunction isCheckboxGroupSectionElement(\n  child: React.ReactNode\n): child is React.ReactElement<CheckboxGroupSectionProps> {\n  return React.isValidElement(child) && child.type === CheckboxGroupSection;\n}\n\nfunction isCheckboxGroupItemElement(\n  child: React.ReactNode\n): child is React.ReactElement<CheckboxGroupItemProps> {\n  return React.isValidElement(child) && child.type === CheckboxGroupItem;\n}\n\nfunction buildModelFromChildren(children: React.ReactNode): CheckboxGroupModel {\n  const model: CheckboxGroupModel = {\n    options: [],\n    sections: [],\n  };\n  const bareItems: React.ReactElement<CheckboxGroupItemProps>[] = [];\n  const childArray = flattenCheckboxGroupChildren(children);\n\n  const flushBareItems = (sectionIndex: number) => {\n    if (bareItems.length === 0) {\n      return;\n    }\n\n    const sectionOptions: Array<{\n      index: number;\n      option: CheckboxGroupOption;\n    }> = [];\n\n    for (const itemChild of bareItems) {\n      sectionOptions.push(appendItemToModel(model, itemChild.props));\n    }\n\n    model.sections.push({\n      disabled: false,\n      key: `section-${sectionIndex}`,\n      options: sectionOptions,\n    });\n    bareItems.length = 0;\n  };\n\n  childArray.forEach((child, childIndex) => {\n    if (isCheckboxGroupItemElement(child)) {\n      bareItems.push(child);\n      return;\n    }\n\n    flushBareItems(childIndex);\n\n    if (!isCheckboxGroupSectionElement(child)) {\n      warnInDevelopment(\n        \"Only CheckboxGroupSection and CheckboxGroupItem children are rendered. Other elements are ignored.\"\n      );\n      return;\n    }\n\n    const { children: sectionChildren, disabled = false, label } = child.props;\n    const sectionOptions: Array<{\n      index: number;\n      option: CheckboxGroupOption;\n    }> = [];\n\n    for (const itemChild of flattenCheckboxGroupChildren(sectionChildren)) {\n      if (!isCheckboxGroupItemElement(itemChild)) {\n        warnInDevelopment(\n          \"CheckboxGroupSection only accepts CheckboxGroupItem children. Other elements are ignored.\"\n        );\n        continue;\n      }\n\n      sectionOptions.push(appendItemToModel(model, itemChild.props));\n    }\n\n    if (sectionOptions.length === 0) {\n      warnInDevelopment(\n        \"CheckboxGroupSection requires at least one CheckboxGroupItem child.\"\n      );\n      return;\n    }\n\n    model.sections.push({\n      disabled,\n      key: label ? `${label}-${childIndex}` : `section-${childIndex}`,\n      label: label?.trim() || undefined,\n      options: sectionOptions,\n    });\n  });\n\n  flushBareItems(model.sections.length);\n\n  return model;\n}\n\nfunction dedupeCheckboxGroupModel(\n  model: CheckboxGroupModel\n): CheckboxGroupModel {\n  const firstOptionByValue = new Map<string, CheckboxGroupOption>();\n  const duplicateValues: string[] = [];\n\n  for (const option of model.options) {\n    if (firstOptionByValue.has(option.value)) {\n      duplicateValues.push(option.value);\n      continue;\n    }\n\n    firstOptionByValue.set(option.value, option);\n  }\n\n  const nextOptions = [...firstOptionByValue.values()];\n  const nextSections = model.sections\n    .map((section) => ({\n      ...section,\n      options: section.options.filter(\n        ({ option }) => firstOptionByValue.get(option.value) === option\n      ),\n    }))\n    .filter((section) => section.options.length > 0);\n\n  if (duplicateValues.length > 0) {\n    warnInDevelopment(\n      `Duplicate option values were ignored: ${[...new Set(duplicateValues)].join(\", \")}.`\n    );\n  }\n\n  return {\n    options: nextOptions,\n    sections: nextSections,\n  };\n}\n\nfunction buildCheckboxGroupModel(\n  children: React.ReactNode | undefined,\n  options?: CheckboxGroupOption[]\n): CheckboxGroupModel {\n  if (children != null && hasCompoundChildren(children)) {\n    return dedupeCheckboxGroupModel(buildModelFromChildren(children));\n  }\n\n  return dedupeCheckboxGroupModel(\n    buildModelFromOptions(\n      (options ?? []).map((option) => ({\n        ...option,\n        value: option.value.trim(),\n      }))\n    )\n  );\n}\n\nfunction getOrderedValues(\n  options: CheckboxGroupOption[],\n  nextSelected: string[]\n): string[] {\n  const nextSet = new Set(nextSelected);\n\n  const orderedVisibleValues = options\n    .filter((option) => nextSet.has(option.value))\n    .map((option) => option.value);\n\n  const optionValues = new Set(options.map((option) => option.value));\n  const extraValues = nextSelected.filter((value) => !optionValues.has(value));\n\n  return [...orderedVisibleValues, ...extraValues];\n}\n\nfunction withReadOnlySelections(\n  options: CheckboxGroupOption[],\n  selectedValues: string[]\n) {\n  const nextSet = new Set(selectedValues);\n\n  for (const option of options) {\n    if (option.readOnly) {\n      nextSet.add(option.value);\n    }\n  }\n\n  return getOrderedValues(options, [...nextSet]);\n}\n\nfunction areValueArraysEqual(left: string[], right: string[]) {\n  if (left.length !== right.length) {\n    return false;\n  }\n\n  return left.every((value, index) => value === right[index]);\n}\n\nfunction getModelStructureKey(model: CheckboxGroupModel) {\n  return model.sections\n    .map((section) => {\n      const optionValues = section.options\n        .map(\n          ({ option }) =>\n            `${option.value}:${option.disabled ? 1 : 0}:${option.readOnly ? 1 : 0}`\n        )\n        .join(\",\");\n\n      return `${section.label ?? \"\"}:${section.disabled}:${optionValues}`;\n    })\n    .join(\"|\");\n}\n\nfunction getChangedOptions(\n  options: CheckboxGroupOption[],\n  previousSelected: string[],\n  nextSelected: string[]\n) {\n  const previousSet = new Set(previousSelected);\n  const nextSet = new Set(nextSelected);\n\n  return options.filter((option) => {\n    const wasSelected = previousSet.has(option.value);\n    const isSelected = nextSet.has(option.value);\n\n    return wasSelected !== isSelected;\n  });\n}\n\nfunction getCommittedValue(\n  isControlled: boolean,\n  value: string[] | undefined,\n  internalValue: string[]\n) {\n  return isControlled ? (value ?? []) : internalValue;\n}\n\nfunction getSectionDisabledForOption(\n  sections: ParsedCheckboxGroupSection[],\n  optionValue: string\n) {\n  for (const section of sections) {\n    if (section.options.some(({ option }) => option.value === optionValue)) {\n      return section.disabled;\n    }\n  }\n\n  return false;\n}\n\nfunction canToggleOption({\n  disabled,\n  isSelected,\n  option,\n  sectionDisabled,\n}: {\n  disabled: boolean;\n  isSelected: boolean;\n  option: CheckboxGroupOption;\n  sectionDisabled: boolean;\n}) {\n  const { rowDisabled, rowReadOnly } = getRowInteractionState({\n    disabled,\n    groupDisabled: sectionDisabled,\n    option,\n  });\n\n  if (rowReadOnly && !isSelected) {\n    return false;\n  }\n\n  return !(rowDisabled || rowReadOnly);\n}\n\nfunction sectionsHaveNamedGroups(sections: ParsedCheckboxGroupSection[]) {\n  return sections.some((section) => Boolean(section.label));\n}\n\nfunction getLabeledSections(sections: ParsedCheckboxGroupSection[]) {\n  return sections.filter((section) => section.label);\n}\n\nfunction getGroupedDisclosureState({\n  expanded,\n  maxVisible,\n  sections,\n  selectedValues,\n}: {\n  expanded: boolean;\n  maxVisible?: number;\n  sections: ParsedCheckboxGroupSection[];\n  selectedValues: string[];\n}) {\n  if (!sectionsHaveNamedGroups(sections)) {\n    return {\n      canCollapse: false,\n      hiddenSectionCount: 0,\n      isSectionVisible: () => true,\n      shouldShowLess: false,\n      shouldShowMore: false,\n    };\n  }\n\n  const labeledSections = getLabeledSections(sections);\n  const unlabeledSectionKeys = new Set(\n    sections.filter((section) => !section.label).map((section) => section.key)\n  );\n  const canCollapse =\n    typeof maxVisible === \"number\" &&\n    maxVisible > 0 &&\n    labeledSections.length > maxVisible;\n\n  if (!canCollapse) {\n    return {\n      canCollapse: false,\n      hiddenSectionCount: 0,\n      isSectionVisible: () => true,\n      shouldShowLess: false,\n      shouldShowMore: false,\n    };\n  }\n\n  const hiddenLabeledSections = labeledSections.slice(maxVisible);\n  const hiddenValues = new Set(\n    hiddenLabeledSections.flatMap((section) =>\n      section.options.map(({ option }) => option.value)\n    )\n  );\n  const hasHiddenSelection = selectedValues.some((value) =>\n    hiddenValues.has(value)\n  );\n  const forcedExpanded = !expanded && hasHiddenSelection;\n  const isExpanded = expanded || forcedExpanded;\n  const visibleLabeledKeys = new Set(\n    (isExpanded ? labeledSections : labeledSections.slice(0, maxVisible)).map(\n      (section) => section.key\n    )\n  );\n\n  return {\n    canCollapse: true,\n    hiddenSectionCount: hiddenLabeledSections.length,\n    isSectionVisible: (sectionKey: string) =>\n      unlabeledSectionKeys.has(sectionKey) ||\n      visibleLabeledKeys.has(sectionKey),\n    shouldShowLess: isExpanded && !hasHiddenSelection,\n    shouldShowMore: !isExpanded,\n  };\n}\n\nfunction resolveRootRenderProps(rootProps: CheckboxRootRenderProps) {\n  const {\n    children: rowChildren,\n    className: rootClassName,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: rootRef,\n    style: rootStyle,\n    ...resolvedRootProps\n  } = rootProps;\n\n  return {\n    resolvedRootProps,\n    rootClassName,\n    rootRef,\n    rootStyle,\n    rowChildren,\n  };\n}\n\nfunction getRowInteractionState({\n  disabled,\n  groupDisabled,\n  option,\n}: {\n  disabled: boolean;\n  groupDisabled: boolean;\n  option: CheckboxGroupOption;\n}) {\n  const rowDisabled = disabled || groupDisabled || Boolean(option.disabled);\n  const rowReadOnly = Boolean(option.readOnly);\n\n  return {\n    rowDisabled,\n    rowReadOnly,\n    rowInteractive: !(rowDisabled || rowReadOnly),\n  };\n}\n\nfunction CheckboxGroupIndicator({\n  checked,\n  iconClassName,\n  invalid,\n  prefersReducedMotion,\n}: {\n  checked: boolean;\n  iconClassName: string;\n  invalid: boolean;\n  prefersReducedMotion: boolean;\n}) {\n  const checkVariants = prefersReducedMotion\n    ? reducedCheckmarkVariants\n    : checkmarkVariants;\n\n  return (\n    <motion.svg\n      aria-hidden\n      className={cn(iconClassName, invalid && !checked && \"text-destructive\")}\n      fill=\"none\"\n      initial={false}\n      viewBox=\"0 0 24 24\"\n    >\n      <motion.path\n        animate={checked ? \"checked\" : \"unchecked\"}\n        d=\"M5 12.5l4.5 4.5L19 7.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth={3}\n        variants={checkVariants}\n      />\n    </motion.svg>\n  );\n}\n\nfunction CheckboxGroupCopy({\n  checked,\n  descriptionId,\n  labelId,\n  labelMotionTransition,\n  option,\n  sizing,\n}: {\n  checked: boolean;\n  descriptionId?: string;\n  labelId: string;\n  labelMotionTransition: typeof labelTransition | typeof instantTransition;\n  option: CheckboxGroupOption;\n  sizing: (typeof sizeStyles)[CheckboxGroupSize];\n}) {\n  const disabledReasonId =\n    option.disabled && option.disabledReason\n      ? `${labelId}-disabled-reason`\n      : undefined;\n\n  return (\n    <div\n      className={cn(\n        \"flex min-w-0 flex-col\",\n        \"transition-transform duration-520 ease-[cubic-bezier(0.22,1,0.36,1)]\",\n        \"group-hover:translate-x-0.5\"\n      )}\n    >\n      <motion.span\n        animate={{ opacity: checked ? 0.55 : 1 }}\n        className={cn(\"font-medium text-foreground\", sizing.label)}\n        id={labelId}\n        transition={labelMotionTransition}\n      >\n        {option.label}\n      </motion.span>\n      {option.description ? (\n        <span className=\"text-muted-foreground text-xs\" id={descriptionId}>\n          {option.description}\n        </span>\n      ) : null}\n      {option.disabled && option.disabledReason ? (\n        <span\n          className=\"text-[11px] text-muted-foreground/90\"\n          id={disabledReasonId}\n        >\n          {option.disabledReason}\n        </span>\n      ) : null}\n    </div>\n  );\n}\n\nfunction CheckboxGroupRow({\n  checked,\n  descriptionId,\n  disabled,\n  form,\n  groupDisabled,\n  invalid,\n  isHidden,\n  labelId,\n  name,\n  option,\n  prefersReducedMotion,\n  sizing,\n}: CheckboxGroupRowProps) {\n  const { rowDisabled, rowInteractive, rowReadOnly } = getRowInteractionState({\n    disabled,\n    groupDisabled,\n    option,\n  });\n  const labelMotionTransition = prefersReducedMotion\n    ? instantTransition\n    : labelTransition;\n  const describedBy = [\n    descriptionId,\n    option.disabled && option.disabledReason\n      ? `${labelId}-disabled-reason`\n      : undefined,\n  ]\n    .filter(Boolean)\n    .join(\" \");\n\n  const whileTap =\n    rowInteractive && !prefersReducedMotion\n      ? {\n          scale: 0.96,\n          y: 0,\n          transition: springTap,\n        }\n      : undefined;\n\n  return (\n    <div aria-hidden={isHidden || undefined} hidden={isHidden || undefined}>\n      <CheckboxPrimitive.Root\n        aria-describedby={describedBy || undefined}\n        aria-labelledby={labelId}\n        aria-readonly={rowReadOnly || undefined}\n        disabled={rowDisabled}\n        form={form}\n        name={name}\n        nativeButton\n        readOnly={rowReadOnly}\n        render={(rootProps) => {\n          const {\n            resolvedRootProps,\n            rootClassName,\n            rootRef,\n            rootStyle,\n            rowChildren,\n          } = resolveRootRenderProps(rootProps);\n\n          return (\n            <motion.button\n              {...resolvedRootProps}\n              aria-invalid={invalid || undefined}\n              className={cn(\n                \"group relative flex w-full appearance-none items-center rounded-lg text-left\",\n                sizing.gap,\n                sizing.row,\n                \"transition-[background-color] duration-480 ease-[cubic-bezier(0.22,1,0.36,1)]\",\n                \"hover:bg-accent/60 dark:hover:bg-accent/60\",\n                \"active:bg-neutral-100 dark:active:bg-neutral-800/78\",\n                \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n                \"disabled:cursor-not-allowed disabled:opacity-50\",\n                \"disabled:active:bg-transparent disabled:hover:bg-transparent\",\n                \"dark:disabled:active:bg-transparent dark:disabled:hover:bg-transparent\",\n                rowReadOnly &&\n                  !rowDisabled &&\n                  \"cursor-default hover:bg-transparent active:bg-transparent\",\n                rootClassName\n              )}\n              ref={(node) => {\n                setRef(rootRef, node);\n              }}\n              style={rootStyle}\n              tabIndex={isHidden ? -1 : undefined}\n              transition={springTap}\n              type=\"button\"\n              whileTap={whileTap}\n            >\n              {rowChildren}\n            </motion.button>\n          );\n        }}\n        value={option.value}\n      >\n        <div\n          className={cn(\n            \"flex shrink-0 items-center justify-center\",\n            sizing.box\n          )}\n        >\n          <div\n            className={cn(\n              \"flex items-center justify-center rounded bg-transparent transition-[border-color] duration-420 ease-[cubic-bezier(0.25,0.85,0.3,1)]\",\n              sizing.box,\n              checked\n                ? \"border-0 bg-transparent\"\n                : [\n                    \"border-2 bg-transparent\",\n                    invalid\n                      ? \"border-destructive/80\"\n                      : \"border-neutral-300/90 group-hover:border-neutral-500/85 dark:border-neutral-600 dark:group-hover:border-neutral-400\",\n                  ]\n            )}\n          >\n            <CheckboxPrimitive.Indicator\n              keepMounted\n              render={(indicatorProps) => {\n                const {\n                  className: indicatorClassName,\n                  children: _children,\n                  onAnimationEnd: _onAnimationEnd,\n                  onAnimationIteration: _onAnimationIteration,\n                  onAnimationStart: _onAnimationStart,\n                  onDrag: _onDrag,\n                  onDragEnd: _onDragEnd,\n                  onDragEnter: _onDragEnter,\n                  onDragExit: _onDragExit,\n                  onDragLeave: _onDragLeave,\n                  onDragOver: _onDragOver,\n                  onDragStart: _onDragStart,\n                  onDrop: _onDrop,\n                  ..._resolvedIndicatorProps\n                } = indicatorProps;\n\n                return (\n                  <CheckboxGroupIndicator\n                    checked={checked}\n                    iconClassName={cn(\n                      sizing.icon,\n                      \"text-primary\",\n                      indicatorClassName\n                    )}\n                    invalid={invalid}\n                    prefersReducedMotion={prefersReducedMotion}\n                  />\n                );\n              }}\n            />\n          </div>\n        </div>\n\n        <CheckboxGroupCopy\n          checked={checked}\n          descriptionId={descriptionId}\n          labelId={labelId}\n          labelMotionTransition={labelMotionTransition}\n          option={option}\n          sizing={sizing}\n        />\n      </CheckboxPrimitive.Root>\n    </div>\n  );\n}\n\nfunction areCheckboxGroupRowPropsEqual(\n  previous: CheckboxGroupRowProps,\n  next: CheckboxGroupRowProps\n) {\n  return (\n    previous.checked === next.checked &&\n    previous.descriptionId === next.descriptionId &&\n    previous.disabled === next.disabled &&\n    previous.form === next.form &&\n    previous.groupDisabled === next.groupDisabled &&\n    previous.invalid === next.invalid &&\n    previous.isHidden === next.isHidden &&\n    previous.labelId === next.labelId &&\n    previous.name === next.name &&\n    previous.option.description === next.option.description &&\n    previous.option.disabled === next.option.disabled &&\n    previous.option.disabledReason === next.option.disabledReason &&\n    previous.option.id === next.option.id &&\n    previous.option.label === next.option.label &&\n    previous.option.readOnly === next.option.readOnly &&\n    previous.option.value === next.option.value &&\n    previous.prefersReducedMotion === next.prefersReducedMotion &&\n    previous.sizing === next.sizing\n  );\n}\n\nconst MemoizedCheckboxGroupRow = React.memo(\n  CheckboxGroupRow,\n  areCheckboxGroupRowPropsEqual\n);\nMemoizedCheckboxGroupRow.displayName = \"CheckboxGroupRow\";\n\nfunction CheckboxGroupSection(_props: CheckboxGroupSectionProps) {\n  return null;\n}\nCheckboxGroupSection.displayName = \"CheckboxGroupSection\";\n\nfunction CheckboxGroupItem(_props: CheckboxGroupItemProps) {\n  return null;\n}\nCheckboxGroupItem.displayName = \"CheckboxGroupItem\";\n\nconst CheckboxGroup = React.forwardRef<HTMLDivElement, CheckboxGroupProps>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedBy,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledBy,\n      children,\n      className,\n      defaultValue,\n      disabled = false,\n      form,\n      invalid = false,\n      maxVisible,\n      name,\n      onChange,\n      options,\n      showLessLabel = \"Show less\",\n      showMoreLabel = \"Show more\",\n      size = \"default\",\n      value,\n    },\n    ref\n  ) => {\n    const generatedId = React.useId();\n    const prefersReducedMotion = useReducedMotion() === true;\n    const sizing = sizeStyles[size];\n    const isControlled = value !== undefined;\n    const model = React.useMemo(\n      () => buildCheckboxGroupModel(children, options),\n      [children, options]\n    );\n    const { options: resolvedOptions, sections } = model;\n    const [internalValue, setInternalValue] = React.useState<string[]>(\n      () => defaultValue ?? []\n    );\n    const normalizedCommittedValue = React.useMemo(\n      () =>\n        withReadOnlySelections(\n          resolvedOptions,\n          getCommittedValue(isControlled, value, internalValue)\n        ),\n      [internalValue, isControlled, resolvedOptions, value]\n    );\n    const [optimisticValue, setOptimisticValue] = React.useState<string[]>(\n      normalizedCommittedValue\n    );\n    const optimisticValueRef = React.useRef(optimisticValue);\n    const [expanded, setExpanded] = React.useState(false);\n\n    optimisticValueRef.current = optimisticValue;\n\n    const modelStructureKey = React.useMemo(\n      () => getModelStructureKey(model),\n      [model]\n    );\n    const collapseSignature = React.useMemo(\n      () => `${maxVisible ?? \"\"}:${modelStructureKey}`,\n      [maxVisible, modelStructureKey]\n    );\n    const collapseSignatureRef = React.useRef(collapseSignature);\n    const selectedSet = React.useMemo(\n      () => new Set(optimisticValue),\n      [optimisticValue]\n    );\n    const disclosure = React.useMemo(\n      () =>\n        getGroupedDisclosureState({\n          expanded,\n          maxVisible,\n          sections,\n          selectedValues: optimisticValue,\n        }),\n      [expanded, maxVisible, optimisticValue, sections]\n    );\n\n    const handleExpand = React.useCallback(() => {\n      setExpanded(true);\n    }, []);\n\n    const handleCollapse = React.useCallback(() => {\n      setExpanded(false);\n    }, []);\n\n    const handleValueChange = React.useCallback(\n      (nextSelected: string[], eventDetails?: { isCanceled?: boolean }) => {\n        if (disabled || eventDetails?.isCanceled) {\n          return;\n        }\n\n        const previousSelected = optimisticValueRef.current;\n        const next = withReadOnlySelections(\n          resolvedOptions,\n          getOrderedValues(resolvedOptions, nextSelected)\n        );\n\n        if (areValueArraysEqual(previousSelected, next)) {\n          return;\n        }\n\n        const changedOptions = getChangedOptions(\n          resolvedOptions,\n          previousSelected,\n          next\n        );\n\n        for (const option of changedOptions) {\n          if (\n            !canToggleOption({\n              disabled,\n              isSelected: next.includes(option.value),\n              option,\n              sectionDisabled: getSectionDisabledForOption(\n                sections,\n                option.value\n              ),\n            })\n          ) {\n            return;\n          }\n        }\n\n        setOptimisticValue(next);\n\n        if (!isControlled) {\n          setInternalValue(next);\n        }\n\n        onChange?.(next);\n      },\n      [disabled, isControlled, onChange, resolvedOptions, sections]\n    );\n\n    React.useEffect(() => {\n      setOptimisticValue((current) =>\n        areValueArraysEqual(current, normalizedCommittedValue)\n          ? current\n          : normalizedCommittedValue\n      );\n    }, [normalizedCommittedValue]);\n\n    React.useEffect(() => {\n      if (isControlled) {\n        return;\n      }\n\n      setInternalValue((current) => {\n        const normalized = withReadOnlySelections(resolvedOptions, current);\n\n        return areValueArraysEqual(current, normalized) ? current : normalized;\n      });\n    }, [isControlled, resolvedOptions]);\n\n    React.useEffect(() => {\n      if (collapseSignatureRef.current === collapseSignature) {\n        return;\n      }\n\n      collapseSignatureRef.current = collapseSignature;\n      setExpanded(false);\n    }, [collapseSignature]);\n\n    if (resolvedOptions.length === 0) {\n      return null;\n    }\n\n    const groupValueProps = isControlled\n      ? { value: optimisticValue }\n      : {\n          defaultValue: withReadOnlySelections(\n            resolvedOptions,\n            defaultValue ?? []\n          ),\n        };\n\n    return (\n      <CheckboxGroupPrimitive\n        {...groupValueProps}\n        aria-describedby={ariaDescribedBy}\n        aria-invalid={invalid || undefined}\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabelledBy}\n        className={cn(\n          componentThemeClassName,\n          \"flex flex-col gap-1.5\",\n          className\n        )}\n        disabled={disabled}\n        onValueChange={handleValueChange}\n        ref={ref}\n      >\n        {sections.map((section) => {\n          const isSectionVisible = disclosure.isSectionVisible(section.key);\n          const sectionBody = section.options.map(({ index, option }) => {\n            const optionId = option.id ?? `${generatedId}-option-${index}`;\n            const labelId = `${optionId}-label`;\n            const descriptionId = option.description\n              ? `${optionId}-description`\n              : undefined;\n            const optionKey = option.id ?? `${option.value}-${index}`;\n\n            return (\n              <MemoizedCheckboxGroupRow\n                checked={\n                  selectedSet.has(option.value) || Boolean(option.readOnly)\n                }\n                descriptionId={descriptionId}\n                disabled={disabled}\n                form={form}\n                groupDisabled={section.disabled}\n                invalid={invalid}\n                isHidden={!isSectionVisible}\n                key={optionKey}\n                labelId={labelId}\n                name={name}\n                option={option}\n                prefersReducedMotion={prefersReducedMotion}\n                sizing={sizing}\n              />\n            );\n          });\n\n          if (section.label) {\n            return (\n              <fieldset\n                className=\"m-0 flex min-w-0 flex-col gap-1 border-0 p-0\"\n                disabled={section.disabled || disabled || undefined}\n                hidden={!isSectionVisible || undefined}\n                key={section.key}\n              >\n                <legend className=\"px-4 pt-2 font-medium text-[11px] text-muted-foreground/80 uppercase tracking-[0.16em]\">\n                  {section.label}\n                </legend>\n                {sectionBody}\n              </fieldset>\n            );\n          }\n\n          return (\n            <fieldset\n              className=\"m-0 flex min-w-0 flex-col gap-1 border-0 p-0\"\n              key={section.key}\n            >\n              {sectionBody}\n            </fieldset>\n          );\n        })}\n\n        {disclosure.shouldShowMore ? (\n          <button\n            aria-expanded={false}\n            className={cn(\n              \"self-start rounded-md px-4 py-2 font-medium text-muted-foreground text-sm transition-colors hover:text-foreground\",\n              \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n            )}\n            onClick={handleExpand}\n            type=\"button\"\n          >\n            {`${showMoreLabel} (${disclosure.hiddenSectionCount})`}\n          </button>\n        ) : null}\n        {disclosure.shouldShowLess ? (\n          <button\n            aria-expanded={true}\n            className={cn(\n              \"self-start rounded-md px-4 py-2 font-medium text-muted-foreground text-sm transition-colors hover:text-foreground\",\n              \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n            )}\n            onClick={handleCollapse}\n            type=\"button\"\n          >\n            {showLessLabel}\n          </button>\n        ) : null}\n      </CheckboxGroupPrimitive>\n    );\n  }\n);\n\nCheckboxGroup.displayName = \"CheckboxGroup\";\n\nexport { CheckboxGroup as checkboxGroup };\nexport { CheckboxGroup, CheckboxGroupItem, CheckboxGroupSection };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Checkbox Group (Base UI)",
  "description": "Checkbox group with the same Iconiq API layered over Base UI primitives, preserving the original row hover, tap spring, checkmark draw, and disclosure behavior."
}
