{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "switch",
  "title": "Switch",
  "description": "Exposure-sheet toggle switch built on native checkbox role=switch with configurable ON/OFF glyph, logical RTL-ready thumb, and stepped 12fps motion.",
  "registryDependencies": [
    "https://frame12.dev/r/frame12-styles.json",
    "https://frame12.dev/r/field.json"
  ],
  "files": [
    {
      "path": "ui/switch.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { useFieldContext } from './field';\n\nexport type SwitchDensity = 'comfortable' | 'compact';\n\nexport interface SwitchProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'role'> {\n  label?: React.ReactNode;\n  hint?: React.ReactNode;\n  error?: React.ReactNode;\n  density?: SwitchDensity;\n  onLabel?: React.ReactNode;\n  offLabel?: React.ReactNode;\n  onCheckedChange?: (checked: boolean) => void;\n  fieldClassName?: string;\n}\n\nconst useIsomorphicLayoutEffect =\n  typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;\n\nexport const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(function Switch(\n  {\n    id,\n    label,\n    hint,\n    error,\n    density,\n    onLabel = 'ON',\n    offLabel = 'OFF',\n    disabled,\n    required,\n    readOnly,\n    className = '',\n    fieldClassName = '',\n    onClick,\n    onChange,\n    onCheckedChange,\n    'aria-describedby': userDescribedBy,\n    'aria-invalid': userAriaInvalid,\n    ...rest\n  },\n  ref,\n) {\n  const fieldCtx = useFieldContext();\n  const autoId = React.useId().replace(/:/g, '');\n  const effectiveDensity: SwitchDensity = density ?? fieldCtx?.density ?? 'comfortable';\n  const isCompact = effectiveDensity === 'compact';\n\n  const hasOwnLabel = Boolean(label);\n  const showOwnLabel = hasOwnLabel;\n  const switchId =\n    id ||\n    (!hasOwnLabel && !fieldCtx?.isControlGroup ? fieldCtx?.id : undefined) ||\n    `f12-switch-${autoId}`;\n\n  const showOwnHint = Boolean(hint && !fieldCtx?.hintId);\n  const showOwnError = Boolean(error && !fieldCtx?.errorId);\n  const hintId = showOwnHint ? `${switchId}-hint` : undefined;\n  const errorId = showOwnError ? `${switchId}-error` : undefined;\n  const describedBy =\n    [userDescribedBy, fieldCtx?.describedBy, hintId, errorId].filter(Boolean).join(' ') ||\n    undefined;\n  const effectiveInvalid = error || fieldCtx?.invalid ? true : userAriaInvalid;\n  const effectiveDisabled = disabled ?? fieldCtx?.disabled;\n  const effectiveRequired = required ?? fieldCtx?.required;\n\n  const registerControlGroup = fieldCtx?.registerControlGroup;\n  const registerControlId = fieldCtx?.registerControlId;\n  useIsomorphicLayoutEffect(() => {\n    if (hasOwnLabel) {\n      registerControlGroup?.();\n    }\n    return registerControlId?.(autoId, id);\n  }, [hasOwnLabel, registerControlGroup, registerControlId, autoId, id]);\n\n  const handleClick = (e: React.MouseEvent<HTMLInputElement>) => {\n    onClick?.(e);\n    if (readOnly) {\n      e.preventDefault();\n    }\n  };\n\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    if (readOnly) {\n      e.preventDefault();\n      return;\n    }\n    onChange?.(e);\n    onCheckedChange?.(e.target.checked);\n  };\n\n  const controlRow = (\n    <div className=\"f12-check-row\" data-disabled={effectiveDisabled ? '' : undefined}>\n      <span\n        className={`f12-switch${isCompact ? ' f12-switch-compact' : ''}`}\n        data-density={isCompact ? 'compact' : undefined}\n      >\n        <input\n          ref={ref}\n          id={switchId}\n          type=\"checkbox\"\n          role=\"switch\"\n          disabled={effectiveDisabled}\n          required={effectiveRequired}\n          readOnly={readOnly}\n          aria-readonly={readOnly ? 'true' : undefined}\n          className={`f12-switch-input${isCompact ? ' f12-switch-input-compact' : ''}${className ? ` ${className}` : ''}`}\n          aria-describedby={describedBy}\n          aria-invalid={effectiveInvalid}\n          onClick={handleClick}\n          onChange={handleChange}\n          {...rest}\n        />\n        <span className=\"f12-switch-track\" aria-hidden=\"true\">\n          <span className=\"f12-switch-glyph f12-switch-glyph-on\">{onLabel}</span>\n          <span className=\"f12-switch-glyph f12-switch-glyph-off\">{offLabel}</span>\n          <span className=\"f12-switch-thumb\" />\n        </span>\n      </span>\n      {showOwnLabel && (\n        <label htmlFor={switchId} className=\"f12-check-label\">\n          {label}\n          {effectiveRequired && (\n            <span className=\"f12-field-req\" aria-hidden=\"true\">\n              {' '}\n              *\n            </span>\n          )}\n        </label>\n      )}\n    </div>\n  );\n\n  if (fieldCtx && !showOwnHint && !showOwnError) {\n    return controlRow;\n  }\n\n  return (\n    <div\n      className={`f12-check-field${isCompact ? ' f12-check-field-compact' : ''}${fieldClassName ? ` ${fieldClassName}` : ''}`}\n      data-density={isCompact ? 'compact' : undefined}\n      data-disabled={effectiveDisabled ? '' : undefined}\n    >\n      {controlRow}\n      {showOwnHint && (\n        <p id={hintId} className=\"f12-hint f12-check-meta\">\n          {hint}\n        </p>\n      )}\n      {showOwnError && (\n        <p id={errorId} className=\"f12-error f12-check-meta\" role=\"alert\">\n          <span className=\"f12-error-flag\" aria-hidden=\"true\">\n            !\n          </span>\n          <span>{error}</span>\n        </p>\n      )}\n    </div>\n  );\n});\n\n(Switch as unknown as { f12IsCheckOrSwitch?: boolean }).f12IsCheckOrSwitch = true;\n",
      "type": "registry:ui",
      "target": "components/frame12/switch.tsx"
    }
  ],
  "type": "registry:ui"
}