{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-switch",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-switch.tsx",
      "content": "\"use client\";\n\nimport { Switch as SwitchPrimitive } from \"@base-ui/react/switch\";\nimport {\n  animate,\n  type MotionValue,\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useTransform,\n} 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 spring = { type: \"spring\" as const, duration: 0.35, bounce: 0.3 };\nconst springFast = { type: \"spring\" as const, duration: 0.15, bounce: 0 };\nconst springSnap = { type: \"spring\" as const, duration: 0.4, bounce: 0.5 };\n\nexport type SwitchSize = \"default\" | \"lg\" | \"sm\";\n\ntype SwitchSizing = {\n  gap: string;\n  label: string;\n  thumbMargin: number;\n  thumbSize: number;\n  thumbTravel: number;\n  trackH: number;\n  trackW: number;\n};\n\nconst sizeStyles: Record<SwitchSize, Omit<SwitchSizing, \"thumbTravel\">> = {\n  sm: {\n    gap: \"gap-2\",\n    label: \"text-xs\",\n    thumbMargin: 2,\n    thumbSize: 16,\n    trackH: 20,\n    trackW: 36,\n  },\n  default: {\n    gap: \"gap-2.5\",\n    label: \"text-sm\",\n    thumbMargin: 2,\n    thumbSize: 20,\n    trackH: 26,\n    trackW: 44,\n  },\n  lg: {\n    gap: \"gap-3\",\n    label: \"text-base\",\n    thumbMargin: 2,\n    thumbSize: 24,\n    trackH: 32,\n    trackW: 52,\n  },\n};\n\nexport interface SwitchProps {\n  \"aria-describedby\"?: string;\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n  checked?: boolean;\n  className?: string;\n  defaultChecked?: boolean;\n  description?: React.ReactNode;\n  descriptionClassName?: string;\n  disabled?: boolean;\n  form?: string;\n  id?: string;\n  inputRef?: React.Ref<HTMLInputElement>;\n  invalid?: boolean;\n  label?: React.ReactNode;\n  labelClassName?: string;\n  labelSide?: \"left\" | \"right\";\n  name?: string;\n  onCheckedChange?: (checked: boolean) => void;\n  readOnly?: boolean;\n  required?: boolean;\n  size?: SwitchSize;\n  value?: string;\n  wrapperClassName?: string;\n}\n\ntype SwitchThumbMotion = {\n  fillOpacity: MotionValue<number>;\n  playPressSquash: () => void;\n  playReleaseSquash: () => void;\n  playToggleSquash: () => void;\n  thumbScaleX: MotionValue<number>;\n  thumbScaleY: MotionValue<number>;\n  thumbX: MotionValue<number>;\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 getSizing(size: SwitchSize): SwitchSizing {\n  const base = sizeStyles[size];\n\n  return {\n    ...base,\n    thumbTravel: Math.max(\n      0,\n      base.trackW - base.thumbSize - base.thumbMargin * 2\n    ),\n  };\n}\n\nfunction preventReadOnlyToggle(event: React.SyntheticEvent) {\n  event.preventDefault();\n  event.stopPropagation();\n}\n\nfunction mergeHandlers<T extends React.SyntheticEvent>(\n  theirs: ((event: T) => void) | undefined,\n  ours: (event: T) => void\n) {\n  return (event: T) => {\n    theirs?.(event);\n    if (!event.defaultPrevented) {\n      ours(event);\n    }\n  };\n}\n\nfunction joinAriaIds(...ids: Array<string | undefined>) {\n  const value = ids.filter(Boolean).join(\" \");\n  return value.length > 0 ? value : undefined;\n}\n\ntype SwitchRootRenderProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n  ref?: React.Ref<HTMLButtonElement>;\n};\n\ntype SwitchThumbRenderProps = React.HTMLAttributes<HTMLSpanElement>;\n\nfunction splitRootRenderProps(rootProps: SwitchRootRenderProps) {\n  const {\n    className: rootClassName,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onClick,\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    onKeyDown,\n    onPointerDown,\n    onPointerLeave,\n    onPointerUp,\n    ref: rootRef,\n    ...resolvedRootProps\n  } = rootProps;\n\n  return {\n    onClick,\n    onKeyDown,\n    onPointerDown,\n    onPointerLeave,\n    onPointerUp,\n    resolvedRootProps,\n    rootClassName,\n    rootRef,\n  };\n}\n\nfunction splitThumbRenderProps(thumbProps: SwitchThumbRenderProps) {\n  const {\n    className: thumbClassName,\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    ...resolvedThumbProps\n  } = thumbProps;\n\n  return { resolvedThumbProps, thumbClassName };\n}\n\nfunction getRowClassName({\n  disabled,\n  gapClassName,\n  readOnly,\n  wrapperClassName,\n}: {\n  disabled: boolean;\n  gapClassName: string;\n  readOnly: boolean;\n  wrapperClassName?: string;\n}) {\n  return cn(\n    componentThemeClassName,\n    \"inline-flex select-none items-center\",\n    gapClassName,\n    !(disabled || readOnly) && \"cursor-pointer\",\n    disabled && \"pointer-events-none cursor-not-allowed opacity-50\",\n    readOnly && !disabled && \"cursor-default\",\n    wrapperClassName\n  );\n}\n\nfunction getTrackClassName({\n  className,\n  disabled,\n  hasText,\n  invalid,\n  readOnly,\n}: {\n  className?: string;\n  disabled: boolean;\n  hasText: boolean;\n  invalid: boolean;\n  readOnly: boolean;\n}) {\n  return cn(\n    componentThemeClassName,\n    \"relative inline-flex shrink-0 items-center rounded-full\",\n    \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n    invalid && \"ring-2 ring-destructive\",\n    !hasText && \"disabled:cursor-not-allowed disabled:opacity-50\",\n    !(disabled || readOnly) && \"cursor-pointer\",\n    readOnly && !disabled && \"cursor-default\",\n    disabled && hasText && \"opacity-100\",\n    className\n  );\n}\n\nfunction useSwitchFieldIds({\n  ariaDescribedBy,\n  ariaLabelledBy,\n  description,\n  id,\n  label,\n}: {\n  ariaDescribedBy?: string;\n  ariaLabelledBy?: string;\n  description?: React.ReactNode;\n  id?: string;\n  label?: React.ReactNode;\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 textLabelId = labelId ?? descriptionId;\n  const hasText = Boolean(label || description);\n\n  return {\n    ariaDescribedBy: joinAriaIds(\n      ariaDescribedBy,\n      description ? descriptionId : undefined\n    ),\n    ariaLabelledBy: hasText\n      ? joinAriaIds(ariaLabelledBy, textLabelId)\n      : ariaLabelledBy,\n    controlId,\n    descriptionId,\n    hasText,\n    labelId,\n  };\n}\n\nfunction useSwitchThumbMotion(\n  displayChecked: boolean,\n  thumbTravel: number,\n  prefersReducedMotion: boolean\n): SwitchThumbMotion {\n  const thumbTravelRef = React.useRef(thumbTravel);\n  thumbTravelRef.current = thumbTravel;\n\n  const thumbX = useMotionValue(displayChecked ? thumbTravel : 0);\n  const thumbScaleX = useMotionValue(1);\n  const thumbScaleY = useMotionValue(1);\n  const fillOpacity = useTransform(thumbX, (x) => {\n    const travel = thumbTravelRef.current;\n\n    if (travel <= 0) {\n      return 0;\n    }\n\n    return Math.min(1, Math.max(0, x / travel));\n  });\n\n  const prevChecked = React.useRef(displayChecked);\n  const prevThumbTravel = React.useRef(thumbTravel);\n\n  React.useEffect(() => {\n    if (prevThumbTravel.current !== thumbTravel) {\n      prevThumbTravel.current = thumbTravel;\n      thumbX.set(displayChecked ? thumbTravel : 0);\n      prevChecked.current = displayChecked;\n    }\n  }, [displayChecked, thumbTravel, thumbX]);\n\n  React.useEffect(() => {\n    const travel = thumbTravelRef.current;\n    const nextX = displayChecked ? travel : 0;\n\n    if (prevChecked.current === displayChecked && thumbX.get() === nextX) {\n      return;\n    }\n\n    prevChecked.current = displayChecked;\n\n    if (prefersReducedMotion) {\n      thumbX.set(nextX);\n      return;\n    }\n\n    const controls = animate(thumbX, nextX, spring);\n\n    return () => {\n      controls.stop();\n    };\n  }, [displayChecked, prefersReducedMotion, thumbX]);\n\n  const playPressSquash = React.useCallback(() => {\n    animate(thumbScaleX, 0.82, springFast);\n    animate(thumbScaleY, 1.1, springFast);\n  }, [thumbScaleX, thumbScaleY]);\n\n  const playReleaseSquash = React.useCallback(() => {\n    animate(thumbScaleX, 1, springSnap);\n    animate(thumbScaleY, 1, springSnap);\n  }, [thumbScaleX, thumbScaleY]);\n\n  const playToggleSquash = React.useCallback(() => {\n    animate(thumbScaleX, 1.15, springFast).then(() => {\n      animate(thumbScaleX, 1, springSnap);\n    });\n    animate(thumbScaleY, 0.88, springFast).then(() => {\n      animate(thumbScaleY, 1, springSnap);\n    });\n  }, [thumbScaleX, thumbScaleY]);\n\n  React.useEffect(() => {\n    return () => {\n      thumbX.stop();\n      thumbScaleX.stop();\n      thumbScaleY.stop();\n    };\n  }, [thumbScaleX, thumbScaleY, thumbX]);\n\n  return {\n    fillOpacity,\n    playPressSquash,\n    playReleaseSquash,\n    playToggleSquash,\n    thumbScaleX,\n    thumbScaleY,\n    thumbX,\n  };\n}\n\nfunction SwitchFieldText({\n  description,\n  descriptionClassName,\n  descriptionId,\n  label,\n  labelClassName,\n  labelId,\n  labelSizeClassName,\n  required,\n}: {\n  description?: React.ReactNode;\n  descriptionClassName?: string;\n  descriptionId?: string;\n  label?: React.ReactNode;\n  labelClassName?: string;\n  labelId?: string;\n  labelSizeClassName: string;\n  required: boolean;\n}) {\n  return (\n    <span className=\"flex min-w-0 flex-col gap-0.5\">\n      {label ? (\n        <span\n          className={cn(\"text-foreground\", labelSizeClassName, labelClassName)}\n          id={labelId}\n        >\n          {label}\n          {required ? (\n            <span aria-hidden className=\"text-destructive\">\n              {\" \"}\n              *\n            </span>\n          ) : null}\n        </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 SwitchTrackLayers({\n  fillOpacity,\n}: {\n  fillOpacity: MotionValue<number>;\n}) {\n  return (\n    <>\n      <span\n        aria-hidden\n        className=\"absolute inset-0 rounded-full\"\n        style={{ backgroundColor: \"var(--ic-accent)\" }}\n      />\n      <motion.span\n        aria-hidden\n        className=\"absolute inset-0 rounded-full\"\n        style={{\n          backgroundColor: \"var(--ic-foreground)\",\n          opacity: fillOpacity,\n        }}\n      />\n    </>\n  );\n}\n\nfunction SwitchThumbVisual({\n  className,\n  motionState,\n  sizing,\n}: {\n  className?: string;\n  motionState: SwitchThumbMotion;\n  sizing: SwitchSizing;\n}) {\n  return (\n    <motion.span\n      aria-hidden\n      className={cn(\"pointer-events-none z-10 block rounded-full\", className)}\n      style={{\n        width: sizing.thumbSize,\n        height: sizing.thumbSize,\n        x: motionState.thumbX,\n        scaleX: motionState.thumbScaleX,\n        scaleY: motionState.thumbScaleY,\n        marginLeft: sizing.thumbMargin,\n        backgroundColor: \"var(--ic-card)\",\n        boxShadow: \"0 1px 4px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.06)\",\n      }}\n    />\n  );\n}\n\nfunction SwitchFieldRow({\n  children,\n  controlId,\n  disabled,\n  gapClassName,\n  labelSide,\n  readOnly,\n  text,\n  wrapperClassName,\n}: {\n  children: React.ReactNode;\n  controlId: string;\n  disabled: boolean;\n  gapClassName: string;\n  labelSide: \"left\" | \"right\";\n  readOnly: boolean;\n  text: React.ReactNode;\n  wrapperClassName?: string;\n}) {\n  return (\n    <label\n      className={getRowClassName({\n        disabled,\n        gapClassName,\n        readOnly,\n        wrapperClassName,\n      })}\n      htmlFor={controlId}\n    >\n      {labelSide === \"left\" ? text : null}\n      {children}\n      {labelSide === \"right\" ? text : null}\n    </label>\n  );\n}\n\nfunction BaseSwitchControl({\n  ariaDescribedBy,\n  ariaLabel,\n  ariaLabelledBy,\n  className,\n  controlId,\n  defaultChecked,\n  disabled,\n  displayChecked,\n  form,\n  hasText,\n  inputRef,\n  invalid,\n  isControlled,\n  motionState,\n  name,\n  onCheckedChange,\n  prefersReducedMotion,\n  readOnly,\n  ref,\n  required,\n  sizing,\n  value,\n}: {\n  ariaDescribedBy?: string;\n  ariaLabel?: string;\n  ariaLabelledBy?: string;\n  className?: string;\n  controlId: string;\n  defaultChecked: boolean;\n  disabled: boolean;\n  displayChecked: boolean;\n  form?: string;\n  hasText: boolean;\n  inputRef?: React.Ref<HTMLInputElement>;\n  invalid: boolean;\n  isControlled: boolean;\n  motionState: SwitchThumbMotion;\n  name?: string;\n  onCheckedChange: (checked: boolean) => void;\n  prefersReducedMotion: boolean;\n  readOnly: boolean;\n  ref: React.ForwardedRef<HTMLButtonElement>;\n  required: boolean;\n  sizing: SwitchSizing;\n  value?: string;\n}) {\n  const isInteractive = !(disabled || readOnly || prefersReducedMotion);\n\n  const handlePointerDown = React.useCallback(() => {\n    if (isInteractive) {\n      motionState.playPressSquash();\n    }\n  }, [isInteractive, motionState]);\n\n  const handlePointerUp = React.useCallback(() => {\n    if (isInteractive) {\n      motionState.playReleaseSquash();\n    }\n  }, [isInteractive, motionState]);\n\n  const getReadOnlyKeyDown = React.useCallback(\n    (theirs?: React.KeyboardEventHandler<HTMLButtonElement>) => {\n      if (!readOnly) {\n        return theirs;\n      }\n\n      return (event: React.KeyboardEvent<HTMLButtonElement>) => {\n        if (event.key === \" \" || event.key === \"Enter\") {\n          preventReadOnlyToggle(event);\n          return;\n        }\n\n        theirs?.(event);\n      };\n    },\n    [readOnly]\n  );\n\n  const trackClassName = getTrackClassName({\n    className,\n    disabled,\n    hasText,\n    invalid,\n    readOnly,\n  });\n\n  const rootCheckedProps = isControlled\n    ? ({ checked: displayChecked } as const)\n    : ({ defaultChecked } as const);\n\n  return (\n    <SwitchPrimitive.Root\n      {...rootCheckedProps}\n      disabled={disabled}\n      form={form}\n      id={controlId}\n      inputRef={inputRef}\n      name={name}\n      nativeButton\n      onCheckedChange={onCheckedChange}\n      readOnly={readOnly}\n      render={(rootProps) => {\n        const {\n          onClick,\n          onKeyDown,\n          onPointerDown,\n          onPointerLeave,\n          onPointerUp,\n          resolvedRootProps,\n          rootClassName,\n          rootRef,\n        } = splitRootRenderProps(rootProps);\n\n        return (\n          <motion.button\n            {...resolvedRootProps}\n            aria-describedby={ariaDescribedBy}\n            aria-invalid={invalid || undefined}\n            aria-label={hasText ? undefined : ariaLabel}\n            aria-labelledby={hasText ? ariaLabelledBy : undefined}\n            aria-readonly={readOnly || undefined}\n            aria-required={required || undefined}\n            className={cn(trackClassName, rootClassName)}\n            dir=\"ltr\"\n            disabled={disabled}\n            initial={false}\n            onClick={readOnly ? preventReadOnlyToggle : onClick}\n            onKeyDown={getReadOnlyKeyDown(onKeyDown)}\n            onPointerDown={mergeHandlers(onPointerDown, handlePointerDown)}\n            onPointerLeave={mergeHandlers(onPointerLeave, handlePointerUp)}\n            onPointerUp={mergeHandlers(onPointerUp, handlePointerUp)}\n            ref={(node) => {\n              setRef(rootRef, node);\n              setRef(ref, node);\n            }}\n            style={{ width: sizing.trackW, height: sizing.trackH }}\n            type=\"button\"\n          >\n            <SwitchTrackLayers fillOpacity={motionState.fillOpacity} />\n            {resolvedRootProps.children}\n          </motion.button>\n        );\n      }}\n      required={required}\n      value={value}\n    >\n      <SwitchPrimitive.Thumb\n        render={(thumbProps) => {\n          const { thumbClassName } = splitThumbRenderProps(thumbProps);\n\n          return (\n            <SwitchThumbVisual\n              className={thumbClassName}\n              motionState={motionState}\n              sizing={sizing}\n            />\n          );\n        }}\n      />\n    </SwitchPrimitive.Root>\n  );\n}\n\nconst Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedBy,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledBy,\n      checked,\n      className,\n      defaultChecked = false,\n      description,\n      descriptionClassName,\n      disabled = false,\n      form,\n      id,\n      inputRef,\n      invalid = false,\n      label,\n      labelClassName,\n      labelSide = \"right\",\n      name,\n      onCheckedChange,\n      readOnly = false,\n      required = false,\n      size = \"default\",\n      value,\n      wrapperClassName,\n    },\n    ref\n  ) => {\n    const prefersReducedMotion = useReducedMotion() === true;\n    const sizing = getSizing(size);\n    const field = useSwitchFieldIds({\n      ariaDescribedBy,\n      ariaLabelledBy,\n      description,\n      id,\n      label,\n    });\n\n    const isControlled = checked !== undefined;\n    const [uncontrolledChecked, setUncontrolledChecked] = React.useState(\n      Boolean(defaultChecked)\n    );\n    const displayChecked = isControlled\n      ? Boolean(checked)\n      : uncontrolledChecked;\n\n    const motionState = useSwitchThumbMotion(\n      displayChecked,\n      sizing.thumbTravel,\n      prefersReducedMotion\n    );\n\n    const handleCheckedChange = React.useCallback(\n      (next: boolean) => {\n        if (disabled || readOnly) {\n          return;\n        }\n\n        if (!prefersReducedMotion) {\n          motionState.playToggleSquash();\n        }\n\n        if (!isControlled) {\n          setUncontrolledChecked(next);\n        }\n\n        onCheckedChange?.(next);\n      },\n      [\n        disabled,\n        isControlled,\n        motionState,\n        onCheckedChange,\n        prefersReducedMotion,\n        readOnly,\n      ]\n    );\n\n    const control = (\n      <BaseSwitchControl\n        ariaDescribedBy={field.ariaDescribedBy}\n        ariaLabel={ariaLabel}\n        ariaLabelledBy={field.ariaLabelledBy}\n        className={className}\n        controlId={field.controlId}\n        defaultChecked={defaultChecked}\n        disabled={disabled}\n        displayChecked={displayChecked}\n        form={form}\n        hasText={field.hasText}\n        inputRef={inputRef}\n        invalid={invalid}\n        isControlled={isControlled}\n        motionState={motionState}\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      />\n    );\n\n    if (!field.hasText) {\n      return control;\n    }\n\n    return (\n      <SwitchFieldRow\n        controlId={field.controlId}\n        disabled={disabled}\n        gapClassName={sizing.gap}\n        labelSide={labelSide}\n        readOnly={readOnly}\n        text={\n          <SwitchFieldText\n            description={description}\n            descriptionClassName={descriptionClassName}\n            descriptionId={field.descriptionId}\n            label={label}\n            labelClassName={labelClassName}\n            labelId={field.labelId}\n            labelSizeClassName={sizing.label}\n            required={required}\n          />\n        }\n        wrapperClassName={wrapperClassName}\n      >\n        {control}\n      </SwitchFieldRow>\n    );\n  }\n);\n\nSwitch.displayName = \"Switch\";\n\nexport { Switch };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Switch (Base UI)",
  "description": "Switch with the same Iconiq API layered over Base UI primitives, preserving the original spring thumb travel, pressure-like squash, and foreground fill sweep."
}
