{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "switch",
  "type": "registry:ui",
  "title": "Switch",
  "description": "On/off control with token-driven track, thumb, and press motion.",
  "dependencies": [
    "@base-ui/react@^1.7.0"
  ],
  "registryDependencies": [
    "https://control-ui.dev/r/core.json"
  ],
  "files": [
    {
      "path": "src/registry/knob-contracts/switch-knobs.ts",
      "target": "@components/control-ui/knob-contracts/switch-knobs.ts",
      "type": "registry:component",
      "content": "// Generated from src/registry/sources/control-ui/recipes/switch.css by scripts/gen-knob-contracts.ts — run `bun run sync:knobs`.\nexport const switchKnobs = [\n  \"--cui-switch-background\",\n  \"--cui-switch-hover-background\",\n  \"--cui-switch-checked-background\",\n  \"--cui-switch-checked-hover-background\",\n  \"--cui-switch-thumb-radius\",\n  \"--cui-switch-thumb-background\",\n] as const;\nexport type SwitchKnobStyle = Partial<Record<(typeof switchKnobs)[number], string>>;\n"
    },
    {
      "path": "src/registry/sources/control-ui/recipes/switch.css",
      "target": "@components/control-ui/styles/recipes/switch.css",
      "type": "registry:file",
      "content": "@layer components {\n  :where([data-control-family=\"switch\"][data-slot=\"root\"]) {\n    --cui-switch-background: oklch(from var(--foreground) l c h / 0.14);\n    --cui-switch-hover-background: oklch(from var(--foreground) l c h / 0.2);\n    --cui-switch-checked-background: var(--primary);\n    --cui-switch-checked-hover-background: oklch(from var(--primary) l c h / 0.9);\n    --cui-switch-thumb-radius: 9999px;\n    --cui-switch-thumb-background: var(--background);\n    border-radius: calc(infinity * 1px);\n    background: var(--cui-switch-background);\n    outline: none;\n    transition: background-color var(--duration-base) var(--ease-standard);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"]:hover) {\n    background: var(--cui-switch-hover-background);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"]:active) {\n    background: oklch(from var(--foreground) l c h / 0.25);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked]) {\n    background: var(--cui-switch-checked-background);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked]:hover) {\n    background: var(--cui-switch-checked-hover-background);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked]:active) {\n    background: oklch(from var(--primary) l c h / 0.8);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-disabled]) {\n    opacity: 0.5;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-disabled]:hover) {\n    background: var(--cui-switch-background);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-disabled][data-checked]:hover) {\n    background: var(--cui-switch-checked-background);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"thumb\"]) {\n    border-radius: var(--cui-switch-thumb-radius);\n    background: var(--cui-switch-thumb-background);\n    box-shadow: var(--shadow-sm);\n    width: calc(var(--spacing) * 4);\n    height: calc(var(--spacing) * 4);\n    transition-property: translate, width;\n    transition-duration: var(--duration-base);\n    transition-timing-function: var(--ease-emphasized);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-disabled] [data-slot=\"thumb\"]) {\n    opacity: 0.8;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"thumb-icon\"]) {\n    color: oklch(from var(--foreground) l c h / 0.7);\n    transition-property: color, opacity;\n    transition-duration: var(--duration-base);\n    transition-timing-function: var(--ease-standard);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked] [data-slot=\"thumb-icon\"]:not([data-switch-icon=\"unchecked\"])) {\n    color: var(--primary-text);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"thumb-icon\"][data-switch-icon=\"unchecked\"]) {\n    color: oklch(from var(--foreground) l c h / 0.6);\n    opacity: 1;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked] [data-switch-icon=\"unchecked\"]) {\n    opacity: 0;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"thumb-icon\"][data-switch-icon=\"checked\"]) {\n    color: var(--primary-text);\n    opacity: 0;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"][data-checked] [data-switch-icon=\"checked\"]) {\n    opacity: 1;\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"thumb\"][data-checked]) {\n    --_switch-thumb-translate: calc(var(--spacing) * 4) 0;\n    translate: var(--_switch-thumb-translate);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"]:active [data-control-family=\"switch\"][data-slot=\"thumb\"]) {\n    width: calc(var(--spacing) * 5);\n  }\n\n  :where([data-control-family=\"switch\"][data-slot=\"root\"]:active [data-control-family=\"switch\"][data-slot=\"thumb\"][data-checked]) {\n    --_switch-thumb-translate: calc(var(--spacing) * 3) 0;\n    translate: var(--_switch-thumb-translate);\n  }\n}\n\n@property --cui-switch-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-switch-hover-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-switch-checked-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-switch-checked-hover-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-switch-thumb-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-switch-thumb-background {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n"
    },
    {
      "path": "src/registry/sources/control-ui/ui/switch.tsx",
      "target": "@components/control-ui/ui/switch.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Switch as SwitchPrimitive } from \"@base-ui/react/switch\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport type { SwitchKnobStyle } from \"@/components/control-ui/knob-contracts/switch-knobs\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\n\nexport type SwitchProps = Omit<\n  {\n    checked?: boolean;\n    defaultChecked?: boolean;\n    onCheckedChange?: (checked: boolean) => void;\n    disabled?: boolean;\n    readOnly?: boolean;\n    required?: boolean;\n    name?: string;\n    value?: string;\n    id?: string;\n    className?: string;\n    icon?: ReactNode;\n    checkedIcon?: ReactNode;\n    uncheckedIcon?: ReactNode;\n    \"aria-label\"?: string;\n    \"aria-labelledby\"?: string;\n    \"aria-describedby\"?: string;\n  },\n  \"style\"\n> & { style?: CSSProperties & SwitchKnobStyle };\n\n// Own anatomy, not restyled Button way Toggle is.\n// thumb stretches on press and its checked offset shrinks by same amount, so it stays flush right while widening.\nexport function Switch({\n  className,\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  disabled,\n  readOnly,\n  required,\n  name,\n  value,\n  id,\n  icon,\n  checkedIcon,\n  uncheckedIcon,\n  ...props\n}: SwitchProps) {\n  const hasStateIcons = checkedIcon !== undefined || uncheckedIcon !== undefined;\n  const hasIcon = icon !== undefined || hasStateIcons;\n  const singleIcon = hasStateIcons ? undefined : icon;\n  const onIcon = checkedIcon ?? icon;\n  const offIcon = uncheckedIcon ?? icon;\n  return (\n    <SwitchPrimitive.Root\n      data-control-ui=\"switch\"\n      data-control-family=\"switch\"\n      data-slot=\"root\"\n      checked={checked}\n      defaultChecked={defaultChecked}\n      onCheckedChange={onCheckedChange}\n      disabled={disabled}\n      readOnly={readOnly}\n      required={required}\n      name={name}\n      value={value}\n      id={id}\n      nativeButton\n      render={<button type=\"button\" />}\n      className={cn(\n        \"relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center p-0.5\",\n        \"data-[disabled]:cursor-not-allowed\",\n        className,\n      )}\n      {...props}\n    >\n      <SwitchPrimitive.Thumb\n        data-control-ui=\"switch\"\n        data-control-family=\"switch\"\n        data-slot=\"thumb\"\n        className=\"pointer-events-none relative block\"\n      >\n        {hasIcon ? <SwitchThumbIcon icon={singleIcon} checkedIcon={onIcon} uncheckedIcon={offIcon} /> : null}\n      </SwitchPrimitive.Thumb>\n    </SwitchPrimitive.Root>\n  );\n}\n\n// decorative — Switch's own role and label carry meaning\nfunction SwitchThumbIcon({ icon, checkedIcon, uncheckedIcon }: { icon?: ReactNode; checkedIcon?: ReactNode; uncheckedIcon?: ReactNode }) {\n  const base = \"absolute inset-0 flex items-center justify-center [&_svg]:size-2.5 [&_svg]:stroke-[2.5]\";\n\n  if (icon !== undefined) {\n    return (\n      <span aria-hidden data-control-ui=\"switch\" data-control-family=\"switch\" data-slot=\"thumb-icon\" className={base}>\n        {icon}\n      </span>\n    );\n  }\n\n  return (\n    <>\n      {uncheckedIcon !== undefined ? (\n        <span\n          aria-hidden\n          data-control-ui=\"switch\"\n          data-control-family=\"switch\"\n          data-slot=\"thumb-icon\"\n          data-switch-icon=\"unchecked\"\n          className={base}\n        >\n          {uncheckedIcon}\n        </span>\n      ) : null}\n      {checkedIcon !== undefined ? (\n        <span\n          aria-hidden\n          data-control-ui=\"switch\"\n          data-control-family=\"switch\"\n          data-slot=\"thumb-icon\"\n          data-switch-icon=\"checked\"\n          className={base}\n        >\n          {checkedIcon}\n        </span>\n      ) : null}\n    </>\n  );\n}\n"
    }
  ],
  "css": {
    "@import \"../components/control-ui/styles/recipes/switch.css\"": {}
  },
  "meta": {}
}
