7.0 KiB
Checkbox
(no submodules)
- checkbox.variants — `checkboxVariants` is the cva (…-
checkbox.variants —
checkboxVariantsis the cva (class-variance-authority) configuration that powers every<Checkbox>render. It is spl… -
checkbox.variants —
checkboxVariantsis the cva (… -
checkbox.variants —
checkboxVariantsis the cva (class-variance-authority) configuration that powers every<Checkbox>render. It is spl… -
checkbox.variants —
checkboxVariantsis the cva (… -
checkbox.variants —
checkboxVariantsis the cva (class-variance-authority) configuration that powers every<Checkbox>render. It is spl… -
checkbox.variants —
checkboxVariantsis the cva (… -
checkbox.variants —
checkboxVariantsis the cva (class-variance-authority) configuration that powers every<Checkbox>render. It is spl… -
checkbox.variants —
checkboxVariantsis 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 withdata-iconslot attribution. - Controlled and uncontrolled use via Radix
checked/defaultChecked/onCheckedChange. aria-checked="mixed"for the indeterminate state per WAI-ARIA spec.disabledblocks 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-nonecollapses transitions forprefers-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>fromlucide-reactrather than inline SVG so consumers replacing the icon library viaThemePlugin.iconLibraryget a coherent swap surface. - The chassis does NOT auto-inject an
aria-labelfor icon-only use — seePurposefor the rationale. - The
data-stateattribute is supplied by Radix; the chassis cva config keysdata-[state=checked]:bg-primary/data-[state=indeterminate]:bg-primaryoff 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.