Checkbox
A form control that lets users select one or more options. Supports checked, unchecked, and indeterminate states. Five sizes, five intents, label/description/error patterns, and a CheckboxGroup compound component with select-all support.
Playground
Installation
pnpm add @tessinaui/uiUsage
import { Checkbox, CheckboxGroup } from "@tessinaui/ui";{/* Standalone */}
<Checkbox label="Accept terms and conditions" />
{/* Controlled */}
<Checkbox
checked={accepted}
onCheckedChange={(val) => setAccepted(val as boolean)}
label="Accept terms"
/>
{/* With description and error */}
<Checkbox
label="Accept terms"
required
intent="error"
errorMessage="You must accept the terms to continue."
/>
{/* Group with select-all */}
<CheckboxGroup
value={selected}
onValueChange={setSelected}
allValues={["a", "b", "c"]}
label="Choose options"
>
<Checkbox name="a" value="a" label="Option A" />
<Checkbox name="b" value="b" label="Option B" />
<Checkbox name="c" value="c" label="Option C" />
</CheckboxGroup>Contact List Example
A common pattern combining Checkbox with Avatar for selecting contacts or friends.
Examples
Default
A basic checkbox with a label.
Sizes
Five sizes from xs to xl; label and icon scale together.
Intents
Semantic colors — none, error, warning, success, and info.
States
Unchecked, checked, indeterminate, and disabled states.
Description and error
Add helper text below the label, or an error message with an alert icon.
Group
A CheckboxGroup renders a labeled fieldset of related checkboxes.
Cards
CheckboxCard turns the entire surface into a selectable option with a title and description.
API Reference
Checkbox props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "filled" | "outline" | "filled" | Unchecked style: filled has bg-secondary, outline is transparent |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Visual size of the checkbox box |
intent | "none" | "error" | "warning" | "success" | "info" | "none" | Semantic color for border / fill |
checked | boolean | — | Controlled checked state |
defaultChecked | boolean | false | Uncontrolled initial state |
onCheckedChange | (checked: boolean) => void | — | Change handler |
indeterminate | boolean | false | Mixed state (renders Minus icon) |
disabled | boolean | false | Prevents interaction |
readOnly | boolean | false | Prevents interaction, accessible |
required | boolean | false | HTML required + asterisk on label |
label | string | — | Text label rendered beside the checkbox |
description | string | — | Helper text below the label |
errorMessage | string | — | Error text (overrides description, adds AlertCircle) |
labelPosition | "left" | "right" | "right" | Which side the label appears on |
dir | "ltr" | "rtl" | — | Text direction for the wrapper |
name | string | — | Input name for form submission |
value | string | — | Value submitted with the form |
parent | boolean | — | Marks as select-all within a CheckboxGroup |
className | string | — | Extra classes on the wrapper <div> |
All other native attributes are forwarded to the underlying <span> (Base UI).
CheckboxGroup props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string[] | — | Controlled list of checked values |
defaultValue | string[] | [] | Uncontrolled initial values |
onValueChange | (value: string[]) => void | — | Change handler |
allValues | string[] | — | All possible values — enables select-all indeterminate logic |
disabled | boolean | false | Disables all checkboxes in the group |
orientation | "vertical" | "horizontal" | "vertical" | Stack direction |
variant | "filled" | "outline" | "filled" | Propagated to child Checkboxes |
size | CheckboxSize | "md" | Propagated to child Checkboxes |
intent | CheckboxIntent | "none" | Propagated to child Checkboxes |
label | string | — | Group legend |
description | string | — | Helper text below legend |
errorMessage | string | — | Error text for the group |
required | boolean | false | Asterisk on group legend |
dir | "ltr" | "rtl" | — | Text direction for the group wrapper |
States
| State | Visual |
|---|---|
| Unchecked | Empty box with border |
| Checked | Filled box + Check icon (white) |
| Indeterminate | Filled box + Minus icon (white) |
| Disabled unchecked | Muted empty box |
| Disabled checked | Muted filled box + muted icon |
| Focus | Ring outline (2px) |
| Hover (unchecked) | Darker border |
| Hover (checked) | Slightly darker fill |
Sizes
| Size | Box | Rounded | Text size |
|---|---|---|---|
xs | 14×14 px | rounded-sm | text-xs |
sm | 16×16 px | rounded | text-sm |
md | 20×20 px | rounded-md | text-sm |
lg | 24×24 px | rounded-md | text-base |
xl | 28×28 px | rounded-lg | text-base |
Intents
| Intent | Unchecked border | Checked fill |
|---|---|---|
none | border-border | bg-primary |
error | border-error | bg-error |
warning | border-warning | bg-warning |
success | border-success | bg-success |
info | border-info | bg-info |
Checkbox Group patterns
Uncontrolled
<CheckboxGroup defaultValue={["email"]} label="Notifications">
<Checkbox name="email" value="email" label="Email" />
<Checkbox name="sms" value="sms" label="SMS" />
<Checkbox name="push" value="push" label="Push" />
</CheckboxGroup>Select all (parent)
const all = ["a", "b", "c"];
const [selected, setSelected] = useState<string[]>([]);
<CheckboxGroup value={selected} onValueChange={setSelected} allValues={all}>
<Checkbox
name="all"
parent
label="Select all"
checked={selected.length === all.length}
indeterminate={selected.length > 0 && selected.length < all.length}
onCheckedChange={(checked) => setSelected(checked ? [...all] : [])}
/>
{all.map((v) => (
<Checkbox key={v} name={v} value={v} label={v} />
))}
</CheckboxGroup>Horizontal
<CheckboxGroup orientation="horizontal" label="Days">
<Checkbox value="mon" label="Mon" />
<Checkbox value="tue" label="Tue" />
<Checkbox value="wed" label="Wed" />
</CheckboxGroup>Accessibility
- Hidden
<input type="checkbox">rendered by Base UI — native form semantics aria-checked="mixed"set automatically for indeterminate statearia-invalidset on checkbox + group whenerrorMessageis presentaria-describedbylinks error messages to their respective controls- Required:
aria-required+ nativerequiredattribute on hidden input - Focus ring visible at all sizes — complies with WCAG 2.1 AA (3:1 contrast)
- Touch target: minimum 44×44 px achieved via padding on the wrapper
- Group rendered as
<fieldset>+<legend>for screen reader context
ChatBubble
AI chat message bubbles with role-aware alignment, streaming state, action buttons, and RTL support.
Chip
Interactive label pills for filtering, tagging, and selection. Four variants, five intents, five sizes, six rounded options, optional remove button, leading icon/avatar/status slots, and RTL support.