{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-checkbox",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-checkbox.tsx",
      "content": "\"use client\";\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\";\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 boxTransition = { type: \"spring\", stiffness: 500, damping: 30 } as const;\nconst instantTransition = { duration: 0 } as const;\nconst labelTransition = { duration: 0.2 } as const;\n\nconst checkmarkVariants = {\n  checked: {\n    pathLength: 1,\n    opacity: 1,\n    transition: {\n      pathLength: { duration: 0.3, ease: [0.65, 0, 0.35, 1] },\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: [0.65, 0, 0.35, 1] },\n      opacity: { duration: 0.1, delay: 0.18 },\n    },\n  },\n} as const;\n\nconst minusVariants = {\n  indeterminate: {\n    pathLength: 1,\n    opacity: 1,\n    transition: {\n      pathLength: { duration: 0.2, ease: [0.65, 0, 0.35, 1] },\n      opacity: { duration: 0.05, delay: 0.06 },\n    },\n  },\n  unchecked: {\n    pathLength: 0,\n    opacity: 0,\n    transition: {\n      pathLength: { duration: 0.2, ease: [0.65, 0, 0.35, 1] },\n      opacity: { duration: 0.1, delay: 0.12 },\n    },\n  },\n} as const;\n\nconst reducedCheckmarkVariants = {\n  checked: { pathLength: 1, opacity: 1 },\n  unchecked: { pathLength: 0, opacity: 0 },\n} as const;\n\nconst reducedMinusVariants = {\n  indeterminate: { pathLength: 1, opacity: 1 },\n  unchecked: { pathLength: 0, opacity: 0 },\n} as const;\n\nexport type CheckboxCheckedState = boolean | \"indeterminate\";\nexport type CheckboxSize = \"default\" | \"lg\" | \"sm\";\n\nconst sizeStyles: Record<\n  CheckboxSize,\n  { box: string; gap: string; icon: string; label: string }\n> = {\n  sm: {\n    box: \"h-4 w-4\",\n    gap: \"gap-2\",\n    icon: \"h-3 w-3\",\n    label: \"text-xs\",\n  },\n  default: {\n    box: \"h-5 w-5\",\n    gap: \"gap-3\",\n    icon: \"h-3.5 w-3.5\",\n    label: \"text-sm\",\n  },\n  lg: {\n    box: \"h-6 w-6\",\n    gap: \"gap-3.5\",\n    icon: \"h-4 w-4\",\n    label: \"text-base\",\n  },\n};\n\nexport interface CheckboxProps {\n  checked?: CheckboxCheckedState;\n  className?: string;\n  defaultChecked?: CheckboxCheckedState;\n  description?: React.ReactNode;\n  descriptionClassName?: string;\n  disabled?: boolean;\n  form?: string;\n  id?: string;\n  invalid?: boolean;\n  label?: React.ReactNode;\n  labelClassName?: string;\n  name?: string;\n  onCheckedChange?: (checked: boolean) => void;\n  readOnly?: boolean;\n  required?: boolean;\n  size?: CheckboxSize;\n  value?: string;\n}\n\ntype CheckboxVisualState = {\n  checked: boolean;\n  indeterminate: boolean;\n};\n\ntype IndicatorVisualState = \"checked\" | \"indeterminate\" | \"unchecked\";\n\ntype CheckboxRootRenderProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n  ref?: React.Ref<HTMLButtonElement>;\n};\n\ntype CheckboxIndicatorRenderProps = React.SVGAttributes<SVGSVGElement> & {\n  children?: React.ReactNode;\n  className?: string;\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 parseCheckedState(state: CheckboxCheckedState | undefined) {\n  if (state === \"indeterminate\") {\n    return {\n      checked: false,\n      indeterminate: true,\n    } satisfies CheckboxVisualState;\n  }\n\n  return {\n    checked: Boolean(state),\n    indeterminate: false,\n  } satisfies CheckboxVisualState;\n}\n\nfunction getIndicatorState(state: CheckboxVisualState): IndicatorVisualState {\n  if (state.indeterminate) {\n    return \"indeterminate\";\n  }\n\n  return state.checked ? \"checked\" : \"unchecked\";\n}\n\nfunction getBoxColors(isFilled: boolean, invalid: boolean) {\n  const backgroundColor = isFilled\n    ? \"var(--color-foreground)\"\n    : \"var(--color-background)\";\n\n  if (invalid) {\n    return { backgroundColor, borderColor: \"var(--color-destructive)\" };\n  }\n\n  const borderColor = isFilled\n    ? \"var(--color-foreground)\"\n    : \"var(--color-border)\";\n\n  return { backgroundColor, borderColor };\n}\n\nfunction getTapScale(\n  disabled: boolean,\n  readOnly: boolean,\n  prefersReducedMotion: boolean\n) {\n  if (disabled || readOnly || prefersReducedMotion) {\n    return undefined;\n  }\n\n  return { scale: 0.88 };\n}\n\nfunction getRowClassName({\n  className,\n  disabled,\n  gapClassName,\n  hasText,\n  readOnly,\n}: {\n  className?: string;\n  disabled: boolean;\n  gapClassName: string;\n  hasText: boolean;\n  readOnly: boolean;\n}) {\n  return cn(\n    componentThemeClassName,\n    \"inline-flex select-none items-start\",\n    gapClassName,\n    hasText && \"cursor-pointer\",\n    disabled && \"cursor-not-allowed opacity-50\",\n    readOnly && !disabled && \"cursor-default\",\n    className\n  );\n}\n\nfunction splitRootRenderProps(rootProps: CheckboxRootRenderProps) {\n  const {\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    ...resolvedRootProps\n  } = rootProps;\n\n  return { rootClassName, rootRef, resolvedRootProps };\n}\n\nfunction splitIndicatorRenderProps(\n  indicatorProps: CheckboxIndicatorRenderProps\n) {\n  return { indicatorClassName: indicatorProps.className };\n}\n\nfunction CheckboxIndicatorIcon({\n  iconClassName,\n  indicatorState,\n  prefersReducedMotion,\n}: {\n  iconClassName: string;\n  indicatorState: IndicatorVisualState;\n  prefersReducedMotion: boolean;\n}) {\n  const checkVariants = prefersReducedMotion\n    ? reducedCheckmarkVariants\n    : checkmarkVariants;\n  const dashVariants = prefersReducedMotion\n    ? reducedMinusVariants\n    : minusVariants;\n\n  return (\n    <motion.svg\n      aria-hidden\n      className={iconClassName}\n      fill=\"none\"\n      initial={false}\n      stroke=\"var(--color-background)\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeWidth={3}\n      viewBox=\"0 0 24 24\"\n    >\n      <motion.path\n        animate={indicatorState === \"checked\" ? \"checked\" : \"unchecked\"}\n        d=\"M5 12.5l4.5 4.5L19 7.5\"\n        variants={checkVariants}\n      />\n      <motion.path\n        animate={\n          indicatorState === \"indeterminate\" ? \"indeterminate\" : \"unchecked\"\n        }\n        d=\"M7 12h10\"\n        variants={dashVariants}\n      />\n    </motion.svg>\n  );\n}\n\nfunction CheckboxFieldText({\n  description,\n  descriptionClassName,\n  descriptionId,\n  label,\n  labelClassName,\n  labelId,\n  labelMotionTransition,\n  labelSizeClassName,\n  required,\n  visual,\n}: {\n  description?: React.ReactNode;\n  descriptionClassName?: string;\n  descriptionId?: string;\n  label?: React.ReactNode;\n  labelClassName?: string;\n  labelId?: string;\n  labelMotionTransition: typeof labelTransition | typeof instantTransition;\n  labelSizeClassName: string;\n  required: boolean;\n  visual: CheckboxVisualState;\n}) {\n  return (\n    <span className=\"flex min-w-0 flex-col gap-0.5 pt-px\">\n      {label ? (\n        <motion.span\n          animate={{ opacity: visual.checked ? 0.55 : 1 }}\n          className={cn(\"text-foreground\", labelSizeClassName, labelClassName)}\n          id={labelId}\n          transition={labelMotionTransition}\n        >\n          {label}\n          {required ? (\n            <span aria-hidden className=\"text-destructive\">\n              {\" \"}\n              *\n            </span>\n          ) : null}\n        </motion.span>\n      ) : null}\n      {description ? (\n        <span\n          className={cn(\n            \"text-muted-foreground text-xs leading-snug\",\n            descriptionClassName\n          )}\n          id={descriptionId}\n        >\n          {description}\n        </span>\n      ) : null}\n    </span>\n  );\n}\n\nfunction BaseCheckboxControl({\n  controlId,\n  descriptionId,\n  disabled,\n  form,\n  indicatorState,\n  invalid,\n  isFilled,\n  labelId,\n  motionTransition,\n  name,\n  onCheckedChange,\n  prefersReducedMotion,\n  readOnly,\n  ref,\n  required,\n  sizing,\n  value,\n  visual,\n}: {\n  controlId: string;\n  descriptionId?: string;\n  disabled: boolean;\n  form?: string;\n  indicatorState: IndicatorVisualState;\n  invalid: boolean;\n  isFilled: boolean;\n  labelId?: string;\n  motionTransition: typeof boxTransition | typeof instantTransition;\n  name?: string;\n  onCheckedChange: (nextChecked: boolean) => void;\n  prefersReducedMotion: boolean;\n  readOnly: boolean;\n  ref: React.ForwardedRef<HTMLButtonElement>;\n  required: boolean;\n  sizing: (typeof sizeStyles)[CheckboxSize];\n  value?: string;\n  visual: CheckboxVisualState;\n}) {\n  const boxColors = getBoxColors(isFilled, invalid);\n  const tapScale = getTapScale(disabled, readOnly, prefersReducedMotion);\n\n  return (\n    <CheckboxPrimitive.Root\n      checked={visual.checked}\n      disabled={disabled}\n      form={form}\n      id={controlId}\n      indeterminate={visual.indeterminate}\n      name={name}\n      nativeButton\n      onCheckedChange={onCheckedChange}\n      readOnly={readOnly}\n      render={(rootProps) => {\n        const { rootClassName, rootRef, resolvedRootProps } =\n          splitRootRenderProps(rootProps as CheckboxRootRenderProps);\n\n        return (\n          <motion.button\n            {...resolvedRootProps}\n            animate={boxColors}\n            aria-describedby={descriptionId}\n            aria-invalid={invalid || undefined}\n            aria-labelledby={labelId}\n            className={cn(\n              \"relative mt-0.5 flex shrink-0 items-center justify-center rounded border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed\",\n              sizing.box,\n              rootClassName\n            )}\n            initial={false}\n            ref={(node) => {\n              setRef(rootRef, node);\n              setRef(ref, node);\n            }}\n            transition={motionTransition}\n            type=\"button\"\n            whileTap={tapScale}\n          />\n        );\n      }}\n      required={required}\n      value={value}\n    >\n      <CheckboxPrimitive.Indicator\n        keepMounted\n        render={(indicatorProps) => {\n          const { indicatorClassName } = splitIndicatorRenderProps(\n            indicatorProps as CheckboxIndicatorRenderProps\n          );\n\n          return (\n            <CheckboxIndicatorIcon\n              iconClassName={cn(sizing.icon, indicatorClassName)}\n              indicatorState={indicatorState}\n              prefersReducedMotion={prefersReducedMotion}\n            />\n          );\n        }}\n      />\n    </CheckboxPrimitive.Root>\n  );\n}\n\nconst Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(\n  (\n    {\n      checked,\n      className,\n      defaultChecked = false,\n      description,\n      descriptionClassName,\n      disabled = false,\n      form,\n      id,\n      invalid = false,\n      label,\n      labelClassName,\n      name,\n      onCheckedChange,\n      readOnly = false,\n      required = false,\n      size = \"default\",\n      value,\n    },\n    ref\n  ) => {\n    const generatedId = React.useId();\n    const controlId = id ?? generatedId;\n    const labelId = label ? `${controlId}-label` : undefined;\n    const descriptionId = description ? `${controlId}-description` : undefined;\n    const prefersReducedMotion = useReducedMotion() === true;\n    const sizing = sizeStyles[size];\n    const [internal, setInternal] = React.useState<CheckboxVisualState>(() =>\n      parseCheckedState(defaultChecked)\n    );\n    const isControlled = checked !== undefined;\n    const visual = isControlled ? parseCheckedState(checked) : internal;\n    const indicatorState = getIndicatorState(visual);\n    const isFilled = visual.indeterminate || visual.checked;\n    const motionTransition = prefersReducedMotion\n      ? instantTransition\n      : boxTransition;\n    const labelMotionTransition = prefersReducedMotion\n      ? instantTransition\n      : labelTransition;\n    const hasText = Boolean(label || description);\n    const Wrapper = hasText ? \"label\" : \"div\";\n\n    const handleCheckedChange = React.useCallback(\n      (nextChecked: boolean) => {\n        if (disabled || readOnly) {\n          return;\n        }\n\n        if (!isControlled) {\n          setInternal({ checked: nextChecked, indeterminate: false });\n        }\n\n        onCheckedChange?.(nextChecked);\n      },\n      [disabled, isControlled, onCheckedChange, readOnly]\n    );\n\n    return (\n      <Wrapper\n        className={getRowClassName({\n          className,\n          disabled,\n          gapClassName: sizing.gap,\n          hasText,\n          readOnly,\n        })}\n        htmlFor={hasText ? controlId : undefined}\n      >\n        <BaseCheckboxControl\n          controlId={controlId}\n          descriptionId={descriptionId}\n          disabled={disabled}\n          form={form}\n          indicatorState={indicatorState}\n          invalid={invalid}\n          isFilled={isFilled}\n          labelId={labelId}\n          motionTransition={motionTransition}\n          name={name}\n          onCheckedChange={handleCheckedChange}\n          prefersReducedMotion={prefersReducedMotion}\n          readOnly={readOnly}\n          ref={ref}\n          required={required}\n          sizing={sizing}\n          value={value}\n          visual={visual}\n        />\n\n        {hasText ? (\n          <CheckboxFieldText\n            description={description}\n            descriptionClassName={descriptionClassName}\n            descriptionId={descriptionId}\n            label={label}\n            labelClassName={labelClassName}\n            labelId={labelId}\n            labelMotionTransition={labelMotionTransition}\n            labelSizeClassName={sizing.label}\n            required={required}\n            visual={visual}\n          />\n        ) : null}\n      </Wrapper>\n    );\n  }\n);\n\nCheckbox.displayName = \"Checkbox\";\n\nexport { Checkbox as checkbox };\nexport { Checkbox };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Checkbox (Base UI)",
  "description": "Checkbox with the same Iconiq API layered over Base UI primitives, preserving the original fill spring, press scale, checkmark draw, and label fade."
}
