Search

Jump to any page.

Forms

Switch

On/off toggle whose thumb springs across and stretches while pressed.

When three or more channels point at the same problem.

Prepare the change for review. Nothing runs without approval.

Tag Khaleeji, Najdi and Hijazi so replies match register.

pnpm dlx shadcn@latest add @paragon/switch
Brings in@base-ui/reactcn

States

The thumb springs across, and stretches while you hold it. Try pressing and holding.

RestHoverFocusPressedInvalidDisabled
off
on
Sizes
registry/paragon/ui/switch.tsx
"use client"

import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
import { cn } from "cn"

/**
 * The thumb springs across and stretches while pressed. Geometry lives in
 * custom properties (--track, --thumb, --thumb-w) so both sizes, both
 * directions and the press stretch share one calculation.
 */
function Switch({
  className,
  size = "default",
  ...props
}: SwitchPrimitive.Root.Props & {
  size?: "sm" | "default"
}) {
  return (
    <SwitchPrimitive.Root
      data-slot="switch"
      data-size={size}
      className={cn(
        "peer group/switch relative inline-flex shrink-0 items-center rounded-full p-(--pad) outline-none",
        "[--pad:2px] [--thumb:1.125rem] [--track:2.375rem] data-[size=sm]:[--thumb:0.875rem] data-[size=sm]:[--track:1.875rem]",
        "[--thumb-w:var(--thumb)] active:[--thumb-w:calc(var(--thumb)+0.3125rem)] motion-reduce:active:[--thumb-w:var(--thumb)]",
        "h-[calc(var(--thumb)+var(--pad)*2)] w-(--track)",
        "bg-input transition-colors duration-(--duration-base) ease-paragon hover:bg-foreground/30",
        "data-checked:bg-primary data-checked:hover:bg-[color-mix(in_oklab,var(--primary),var(--background)_16%)]",
        "after:absolute after:-inset-x-2 after:-inset-y-2.5",
        "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:outline-solid",
        "aria-invalid:ring-2 aria-invalid:ring-destructive/60",
        "data-disabled:pointer-events-none data-disabled:opacity-50",
        "group-has-[:focus-visible]/field-label:outline-none",
        className
      )}
      {...props}
    >
      <SwitchPrimitive.Thumb
        data-slot="switch-thumb"
        className={cn(
          "pointer-events-none block h-(--thumb) w-(--thumb-w) rounded-full bg-card shadow-[0_1px_2px_rgb(0_0_0/0.2),0_0_0_0.5px_rgb(0_0_0/0.04)]",
          "[transition:translate_var(--duration-slow)_var(--ease-spring),width_var(--duration-base)_var(--ease-paragon),background-color_var(--duration-base)_var(--ease-paragon)]",
          "data-checked:translate-x-[calc(var(--track)-var(--thumb-w)-var(--pad)*2)] rtl:data-checked:-translate-x-[calc(var(--track)-var(--thumb-w)-var(--pad)*2)]",
          "motion-reduce:transition-none",
          "dark:bg-foreground dark:data-checked:bg-primary-foreground"
        )}
      />
    </SwitchPrimitive.Root>
  )
}

export { Switch }