Skip to content

InputsCheckbox

Checkbox

Native input, custom skin. The label is the click target, not just the box.

Native <input>IndeterminateGroup variant

States

Unchecked, checked, indeterminate, disabled. The label is the click target; the box is the visual indicator.

States

Anatomy

  • 16 × 16 px box, 2 px radius, 1 px --border at rest
  • Checked: --foreground background, --background check
  • Indeterminate: --foreground background, --background bar
  • Focus ring is the system default applied to the input
  • Label is set in 0.9375 rem, attached by htmlFor

Implementation

checkbox.tsx
import { forwardRef, useEffect, useRef, type InputHTMLAttributes } from "react"
import { Check } from "lucide-react"
import SiteIcon from "@/components/site-icon"

interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
  label: string
  indeterminate?: boolean
}

export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
  function Checkbox({ label, indeterminate, className, ...props }, ref) {
    const innerRef = useRef<HTMLInputElement | null>(null)
    useEffect(() => {
      if (innerRef.current) innerRef.current.indeterminate = !!indeterminate
    }, [indeterminate])
    return (
      <label className={cn("ds-checkbox", className)}>
        <input
          ref={(node) => {
            if (typeof ref === "function") ref(node)
            innerRef.current = node
          }}
          type="checkbox"
          {...props}
        />
        <span className="ds-checkbox__box" aria-hidden="true">
          {indeterminate ? <span className="ds-checkbox__bar" /> : <SiteIcon icon={Check} size="xs" />}
        </span>
        <span className="ds-checkbox__label">{label}</span>
      </label>
    )
  },
)

Props

PropTypeDefaultDescription
labelstringNoneRequired: the label
checkedbooleanfalseChecked state
indeterminatebooleanfalseIndeterminate state
disabledbooleanfalseDisabled state