Files
skills/docs/ui/react/react-ui/src/components/checkbox/checkbox.md
2026-05-29 08:20:56 +01:00

7.0 KiB

Checkbox

(no submodules)

- checkbox.variants — `checkboxVariants` is the cva (…
  • checkbox.variants — checkboxVariants is the cva (class-variance-authority) configuration that powers every <Checkbox> render. It is spl…

  • checkbox.variants — checkboxVariants is the cva (…

  • checkbox.variants — checkboxVariants is the cva (class-variance-authority) configuration that powers every <Checkbox> render. It is spl…

  • checkbox.variants — checkboxVariants is the cva (…

  • checkbox.variants — checkboxVariants is the cva (class-variance-authority) configuration that powers every <Checkbox> render. It is spl…

  • checkbox.variants — checkboxVariants is the cva (…

  • checkbox.variants — checkboxVariants is the cva (class-variance-authority) configuration that powers every <Checkbox> render. It is spl…

  • checkbox.variants — checkboxVariants is the cva (class-variance-authority) configuration that powers every <Checkbox> render. It is spl…

Purpose

<Checkbox> is the chassis form-checkbox primitive built on @radix-ui/react-checkbox. Radix supplies the a11y semantics (role="checkbox", aria-checked reflecting true | false | "mixed", native Space activation, disabled propagation); the chassis layers theme-driven variant resolution, the indicator render, and the cva size axis so consumers can scale checkboxes inside data-table headers, settings panes, and dense forms without manual class overrides.

The indicator picks its icon based on the controlled checked value: <CheckIcon> for the boolean-true case and <MinusIcon> for the indeterminate case. Both icons carry a data-icon slot attribute so consumer themes may swap them. Refs are forwarded to the Radix Root which renders a styled <button>.

Icon-only checkboxes (no visible label sibling) MUST supply an aria-label so screen readers can announce the control. The chassis does NOT inject a fallback because the check icon's purpose is domain-specific — only the consumer knows what is being toggled.

Features

  • Three sizes (sm, default, lg) scaling the box and inner icon proportionally.
  • Indicator render with <CheckIcon> for the checked state and <MinusIcon> for the indeterminate state — both with data-icon slot attribution.
  • Controlled and uncontrolled use via Radix checked / defaultChecked / onCheckedChange.
  • aria-checked="mixed" for the indeterminate state per WAI-ARIA spec.
  • disabled blocks pointer + keyboard input and decays opacity.
  • data-state="checked|unchecked|indeterminate" mirrored on the rendered button so consumer cva configs / CSS can react.
  • Refs forwarded to the Radix Root.
  • motion-reduce:transition-none collapses transitions for prefers-reduced-motion: reduce.

Usage: Basic Example

import { Checkbox } from "@suaveplan/react-ui";

export function AcceptTerms() {
    return (
        <div className="flex items-center gap-2">
            <Checkbox id="terms" />
            <label htmlFor="terms">Accept terms</label>
        </div>
    );
}

Usage: Advanced Example

import { Checkbox } from "@suaveplan/react-ui";
import { useMemo, useState } from "react";

interface Permission {
    readonly id: string;
    readonly label: string;
}

export function PermissionMatrix({ items }: { items: readonly Permission[] }) {
    const [selected, setSelected] = useState<readonly string[]>([]);

    const allChecked = selected.length === items.length;
    const someChecked = selected.length > 0 && !allChecked;
    const headerState = allChecked ? true : someChecked ? "indeterminate" : false;

    const toggleAll = () =>
        setSelected(allChecked ? [] : items.map((i) => i.id));
    const toggleOne = (id: string) =>
        setSelected((prev) =>
            prev.includes(id) ? prev.filter((p) => p !== id) : [...prev, id]
        );

    return (
        <table>
            <thead>
                <tr>
                    <th>
                        <Checkbox
                            aria-label="select all"
                            checked={headerState}
                            onCheckedChange={toggleAll}
                        />
                    </th>
                    <th>Permission</th>
                </tr>
            </thead>
            <tbody>
                {items.map((item) => (
                    <tr key={item.id}>
                        <td>
                            <Checkbox
                                aria-label={`select ${item.label}`}
                                checked={selected.includes(item.id)}
                                onCheckedChange={() => toggleOne(item.id)}
                            />
                        </td>
                        <td>{item.label}</td>
                    </tr>
                ))}
            </tbody>
        </table>
    );
}

API Reference

<Checkbox>

forwardRef<ComponentRef<typeof CheckboxPrimitive.Root>, CheckboxProps> — wraps Radix Root, renders a <button role="checkbox"> containing the Radix Indicator with the appropriate icon.

CheckboxProps

Inherits every prop from Radix CheckboxPrimitive.Root except asChild. Adds:

Prop Type Default Description
size "sm" | "default" | "lg" "default" Visual variant scaling box + icon.

Common Radix props consumers will use:

Prop Type Default Description
checked boolean | "indeterminate" uncontrolled Controlled state.
defaultChecked boolean false Initial state for uncontrolled use.
onCheckedChange (checked: boolean | "indeterminate") => void none Fires on state change.
disabled boolean false Blocks input + decays opacity.
required boolean false Native form-field required.
name string none Native form-field name.
value string "on" Native form-field value when checked.

checkboxVariants

The bare cva resolver. Exported so external consumers can re-use the variants directly.

Implementation Notes

  • The variants module is intentionally separate (checkbox.variants.ts) so an ejected source copy keeps the cva config in a dedicated file.
  • The indicator uses <CheckIcon> / <MinusIcon> from lucide-react rather than inline SVG so consumers replacing the icon library via ThemePlugin.iconLibrary get a coherent swap surface.
  • The chassis does NOT auto-inject an aria-label for icon-only use — see Purpose for the rationale.
  • The data-state attribute is supplied by Radix; the chassis cva config keys data-[state=checked]:bg-primary / data-[state=indeterminate]:bg-primary off it so checked + indeterminate render with the same primary background.
  • Refs target the Radix Root (the <button> element) — consumers can .focus() the checkbox programmatically through the forwarded ref.