Search

Jump to any page.

Forms

Checkbox

Checkbox whose tick draws itself in, with indeterminate, invalid and choice-card states.

Patterns only. Never a customer’s words or identity.

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

States

The tick draws itself in; the box springs when pressed. Focus is the same 2px offset ring as Button.

RestHoverFocusPressedInvalidDisabled
unchecked
checked
indeterminate

Group

The parent goes indeterminate when only some channels are on.

Channels Constellation listens to
registry/paragon/ui/checkbox.tsx
"use client"

import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
import { cn } from "cn"

/**
 * The tick draws itself in when checked and fades out when cleared.
 * Pass `indeterminate` for a parent of a partially selected group.
 */
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
  return (
    <CheckboxPrimitive.Root
      data-slot="checkbox"
      className={cn(
        "peer relative flex size-[1.125rem] shrink-0 items-center justify-center rounded-[0.3125rem] border border-input bg-card text-primary-foreground outline-none",
        "[transition:background-color_var(--duration-base)_var(--ease-paragon),border-color_var(--duration-base)_var(--ease-paragon),scale_var(--duration-slow)_var(--ease-spring)]",
        "after:absolute after:-inset-2.5",
        "hover:border-foreground/45",
        "active:scale-90 active:[transition-duration:var(--duration-fast)] motion-reduce:active:scale-100",
        "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:outline-solid",
        "data-checked:border-primary data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary",
        "aria-invalid:border-destructive aria-invalid:focus-visible:outline-destructive aria-invalid:data-checked:border-primary",
        "data-disabled:pointer-events-none data-disabled:opacity-50",
        "dark:bg-input/15",
        // Inside a choice card the card carries the focus ring instead.
        "group-has-[:focus-visible]/field-label:outline-none",
        className
      )}
      {...props}
    >
      <CheckboxPrimitive.Indicator
        data-slot="checkbox-indicator"
        className="group/indicator flex items-center justify-center transition-opacity duration-(--duration-fast) data-ending-style:opacity-0"
      >
        <svg viewBox="0 0 12 12" fill="none" aria-hidden="true" className="size-3 stroke-current">
          <path
            d="M2.5 6.25 5 8.75 9.75 3.5"
            pathLength={1}
            strokeWidth={1.75}
            strokeLinecap="round"
            strokeLinejoin="round"
            className="transition-[stroke-dashoffset] duration-(--duration-slow) ease-paragon [stroke-dasharray:1] [stroke-dashoffset:0] group-data-indeterminate/indicator:hidden group-data-starting-style/indicator:[stroke-dashoffset:1] motion-reduce:transition-none"
          />
          <path
            d="M3 6h6"
            pathLength={1}
            strokeWidth={1.75}
            strokeLinecap="round"
            className="hidden transition-[stroke-dashoffset] duration-(--duration-base) ease-paragon [stroke-dasharray:1] [stroke-dashoffset:0] group-data-indeterminate/indicator:block group-data-starting-style/indicator:[stroke-dashoffset:1] motion-reduce:transition-none"
          />
        </svg>
      </CheckboxPrimitive.Indicator>
    </CheckboxPrimitive.Root>
  )
}

export { Checkbox }