{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"switch","title":"Switch","description":"Base UI-backed on/off preference control.","dependencies":["@base-ui/react@1.7.0","class-variance-authority@0.7.1"],"registryDependencies":["@siteplane/base"],"files":[{"path":"packages/ui/src/components/switch/switch.tsx","content":"\"use client\";\n\nimport { Switch as SwitchPrimitive } from \"@base-ui/react/switch\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport {\n  siteplaneUIDefaults,\n  useResolvedControlSize,\n} from \"../../system/provider/siteplane-ui-provider\";\nimport {\n  choiceMenuItemClassName,\n  choiceMenuItemSwitchGridClassName,\n  choiceMenuItemSwitchLabelClassName,\n  menuRowLabel,\n} from \"../../system/styles/choice-menu-item\";\nimport { cn, type WithStringClassName } from \"../../system/utils/cn\";\nimport { baseClass, slotClass } from \"../../system/utils/slot-class\";\nimport type { KitSwitchProps } from \"./switch-internal\";\n\nimport \"./switch.css\";\n\nexport const switchVariants = cva(\n  \"sp-switch-motion inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 cursor-pointer items-center p-0.5 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input not-data-disabled:data-unchecked:hover:bg-input/80 dark:not-data-disabled:data-unchecked:hover:bg-white/12\",\n  {\n    defaultVariants: { size: siteplaneUIDefaults.controlSize },\n    variants: {\n      size: {\n        md: \"[--thumb-size:--spacing(5)] sm:[--thumb-size:--spacing(4)]\",\n        lg: \"[--thumb-size:--spacing(6)] rounded-xl sm:[--thumb-size:--spacing(5)] sm:rounded-lg\",\n        sm: \"[--thumb-size:--spacing(4)] sm:[--thumb-size:--spacing(3.5)]\",\n        xl: \"[--thumb-size:--spacing(7)] sm:[--thumb-size:--spacing(6)]\",\n      },\n    },\n  },\n);\n\n// The thumb starts at the track's inline start and travels toward its end\n// when checked: in a right-to-left page it starts on the right and travels\n// left, and it stretches from its leading edge there too.\nexport const switchThumbClassName =\n  \"pointer-events-none block aspect-square h-full origin-left rounded-[inherit] bg-background shadow-sm/5 will-change-transform in-[[data-foundation=switch][data-checked]]:origin-[var(--thumb-size)_50%] in-[[data-foundation=switch][data-checked]]:translate-x-[calc(var(--thumb-size)-4px)] rtl:origin-right rtl:in-[[data-foundation=switch][data-checked]]:origin-[calc(100%-var(--thumb-size))_50%] rtl:in-[[data-foundation=switch][data-checked]]:-translate-x-[calc(var(--thumb-size)-4px)]\";\n\n// The standalone control draws its track and thumb, their sizes and the\n// thumb's travel from design-rework.css ([data-slot=\"switch\"]); the utilities\n// above style the menu tracks only. Both tracks take one edge from that\n// stylesheet, the standalone control's 3:1 edge, which a menu track finds by\n// its switch-track slot. Every thumb stretches from its leading edge while the\n// switch or its label is pressed, in either direction (design-rework.css).\nconst switchControlClassName =\n  \"sp-switch-motion inline-flex shrink-0 cursor-pointer items-center data-disabled:cursor-not-allowed\";\n\nconst switchControlThumbClassName =\n  \"pointer-events-none block aspect-square origin-left will-change-transform in-[[data-foundation=switch][data-checked]]:origin-right rtl:origin-right rtl:in-[[data-foundation=switch][data-checked]]:origin-left\";\n\nexport const switchMenuTrackClassName =\n  \"col-start-2 in-[[data-foundation=switch][data-checked]]:bg-primary in-[[data-foundation=switch][data-unchecked]]:bg-input\";\n\nexport interface SwitchProps\n  extends\n    Omit<WithStringClassName<SwitchPrimitive.Root.Props>, \"size\">,\n    VariantProps<typeof switchVariants> {}\n\nexport function Switch(switchProps: SwitchProps): React.ReactElement {\n  // Menu and drawer rows pass the internal menu-item profile through\n  // KitSwitch (switch-internal.ts), the same function.\n  const {\n    children,\n    className,\n    profile = \"control\",\n    size,\n    ...props\n  } = switchProps as KitSwitchProps;\n  const resolvedSize = useResolvedControlSize(size);\n\n  return (\n    <SwitchPrimitive.Root\n      className={cn(\n        // A menu row shows the focus once, as a whole, like the check and\n        // radio rows (T3); its track takes no ring of its own.\n        slotClass(\"switch\", props),\n        baseClass(\"switch\", props),\n        profile === \"menu-item\"\n          ? cn(choiceMenuItemClassName, choiceMenuItemSwitchGridClassName)\n          : switchControlClassName,\n        className,\n      )}\n      data-foundation=\"switch\"\n      data-profile={profile}\n      data-size={resolvedSize}\n      data-slot=\"switch\"\n      {...props}\n    >\n      {profile === \"menu-item\" ? (\n        <>\n          <span className={choiceMenuItemSwitchLabelClassName}>\n            {menuRowLabel(children)}\n          </span>\n          <span\n            aria-hidden=\"true\"\n            className={cn(\n              \"sp-switch-track\",\n              switchVariants({ size: resolvedSize }),\n              switchMenuTrackClassName,\n            )}\n            data-slot=\"switch-track\"\n          >\n            <SwitchPrimitive.Thumb\n              className={cn(\"sp-switch-thumb\", switchThumbClassName)}\n              data-slot=\"switch-thumb\"\n            />\n          </span>\n        </>\n      ) : (\n        <SwitchPrimitive.Thumb\n          className={cn(\"sp-switch-thumb\", switchControlThumbClassName)}\n          data-slot=\"switch-thumb\"\n        />\n      )}\n    </SwitchPrimitive.Root>\n  );\n}\n\nexport { SwitchPrimitive };\n","type":"registry:ui","target":"~/src/siteplane/components/switch/switch.tsx"},{"path":"packages/ui/src/components/switch/switch-internal.ts","content":"import type * as React from \"react\";\n\nimport { Switch, type SwitchProps } from \"./switch\";\n\n/*\n * Kit-internal Switch. Not public API: the switch index does not export this\n * module, and its names and profiles may change in any release. Menu and\n * Drawer switch rows render a Switch with the menu-item profile.\n */\nexport type SwitchProfile = \"control\" | \"menu-item\";\n\nexport type KitSwitchProps = SwitchProps & { profile?: SwitchProfile };\n\n/** Switch with the kit's composition profile; the same component. */\nexport const KitSwitch = Switch as (\n  props: KitSwitchProps,\n) => React.ReactElement;\n","type":"registry:ui","target":"~/src/siteplane/components/switch/switch-internal.ts"},{"path":"packages/ui/src/components/switch/switch.css","content":"@layer theme, base, components, siteplane, utilities;\n\n@layer components {\n  .sp-switch-motion {\n    --sp-semantic-color-background: var(--color-white);\n    --sp-semantic-color-input: rgb(0 0 0 / 10%);\n    --sp-semantic-color-primary: oklch(0.335 0.061 267.9);\n    --sp-semantic-color-ring: var(--color-neutral-400);\n    /* The track's fill changes on the state step, as Checkbox and Radio do,\n     * so the track and the thumb finish together. */\n    transition-duration: var(--motion-duration-fast);\n    transition-property: background-color, box-shadow;\n    transition-timing-function: var(--motion-ease-state);\n  }\n\n  .dark .sp-switch-motion,\n  [data-theme=\"dark\"] .sp-switch-motion {\n    --sp-semantic-color-background: color-mix(\n      in srgb,\n      var(--color-neutral-950) 95%,\n      var(--color-white)\n    );\n    --sp-semantic-color-input: rgb(255 255 255 / 8%);\n    --sp-semantic-color-ring: var(--color-neutral-500);\n  }\n\n  /* The thumb of a menu row travels over the fast step on the browser's\n   * default curve and stretches a micro step after a press. */\n  .sp-switch-motion .sp-switch-thumb {\n    transition:\n      translate var(--motion-duration-fast) var(--motion-ease-navigation),\n      scale var(--motion-duration-micro) var(--motion-ease-navigation)\n        var(--motion-duration-micro),\n      transform-origin var(--motion-duration-fast) var(--motion-ease-navigation);\n  }\n\n  /* Rulebook I5: a switch before text centres on the first text line, also\n   * when the text wraps, as the Checkbox does (checkbox.css). The lg and xl\n   * switches are taller than the 21 px line, so they start at its top and a\n   * one-line label keeps its height. */\n  :where(label, .sp-label, .sp-field-label)\n    > .sp-base-switch[data-profile=\"control\"]:first-child,\n  :where(label, .sp-label, .sp-field-label)\n    > :where(span, div)\n    > .sp-base-switch[data-profile=\"control\"]:first-child,\n  .sp-base-switch[data-profile=\"control\"]:first-child:has(\n      ~ :is(.sp-label, .sp-field-label),\n      ~ * :is(.sp-label, .sp-field-label)\n    ) {\n    align-self: flex-start;\n    margin-block: max(\n      0px,\n      calc((1lh - var(--sp-rw-switch-thumb) - var(--sp-rw-space-1)) / 2)\n    );\n  }\n\n  /* Beside its label, the switch does not inherit the label's line but its\n   * container's. It takes the 21 px line of a label next to a choice control\n   * (S13), so the 1lh above measures the label's first line. */\n  .sp-base-switch[data-profile=\"control\"]:first-child:has(\n      ~ :is(.sp-label, .sp-field-label),\n      ~ * :is(.sp-label, .sp-field-label)\n    ) {\n    line-height: var(--sp-rw-line-21);\n  }\n\n  html[data-motion-reduce=\"true\"] .sp-switch-motion,\n  html[data-motion-reduce=\"true\"] .sp-switch-motion .sp-switch-thumb {\n    animation: none;\n    transition-delay: 0ms;\n    transition-duration: 1ms;\n  }\n}\n\n/* The reduced-motion override sits in the kit layer, where it keeps its order\n * against design-rework.css. */\n@layer siteplane {\n  @media (prefers-reduced-motion: reduce) {\n    .sp-switch-motion,\n    .sp-switch-motion .sp-switch-thumb {\n      animation: none;\n      transition-delay: 0ms;\n      transition-duration: 1ms;\n    }\n  }\n}\n","type":"registry:ui","target":"~/src/siteplane/components/switch/switch.css"},{"path":"packages/ui/src/components/switch/index.ts","content":"export * from \"./switch\";\n","type":"registry:ui","target":"~/src/siteplane/components/switch/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/switch.md","categories":["Forms & selection"],"meta":{"avoidWhen":["Avoid for a choice that applies only after a submit; use Checkbox.","Avoid for several related choices; use Checkbox Group, or Radio Group for exactly one."],"description":"Base UI-backed on/off preference control.","knownLimitations":["Switch has no indeterminate state."],"providerNotes":[],"releaseStatus":"stable","searchTerms":["switch"],"useWhen":["Use for a setting that is on or off and applies at once, such as a notification preference."]}}