AntaCheckbox
Switch to dark theme
Search documentation
On this page

Checkbox Documentation has not yet been reviewed by a human.

Checkboxes let users select one or more items from a set, or toggle an option on or off. Use them for multiple independent choices; for a single immediate setting, use a switch instead.

Use defaultChecked for an initial choice, or pair checked with onStateChange to control it. Pass 'indeterminate' for a mixed selection, such as a parent checkbox whose children have different states.

Playground

Tone

tone colors the mark — the checked-box fill and, for a tinted tone, the unselected box border (a light shade of the fill that darkens on hover and active, like the neutral border; neutral keeps a grey border). The label and hint stay neutral. Six named tones (neutral default), or pass any CSS color for a custom tone.

NeutralBrandInfoSuccessWarningCriticalCustom
<Checkbox defaultChecked tone="neutral">Neutral</Checkbox>
<Checkbox defaultChecked tone="brand">Brand</Checkbox>
<Checkbox defaultChecked tone="critical">Critical</Checkbox>
<Checkbox defaultChecked tone="mediumaquamarine">Custom color</Checkbox>

Selected-only tone

toneScope="selected" applies the checkbox’s tone to the checked mark only — the empty box stays neutral grey until it’s checked. Use it when a resting tinted border would read as a validation error.

Off — neutralOn — brandOn — critical
<Checkbox tone="brand" toneScope="selected">Off stays neutral</Checkbox>
<Checkbox defaultChecked tone="critical" toneScope="selected">Checked mark tinted</Checkbox>

Coloring the text

There’s no text-tone prop. To tint the label, set color on the checkbox with a theme-aware --text-N-{tone} token (the label inherits it); for the hint, add a color rule on the a-checkbox-hint child. Pair either with tone to tint the whole control.

CriticalDeletes everything
/* Mark via the prop, text via a plain color rule — the label inherits the host
color, the hint is targeted directly. The class is just for the demo — swap
your own selector. */
a-checkbox.toned-text { color: var(--text-1-critical); }
a-checkbox.toned-text a-checkbox-hint { color: var(--text-3-critical); }

Size

Three sizes scale the box and the label + hint type together: small (14px box / 13px label), medium (16px / 15px, the default), and large (18px / 17px).

SmallMediumLarge
<Checkbox defaultChecked size="small">Small</Checkbox>
<Checkbox defaultChecked>Medium</Checkbox>
<Checkbox defaultChecked size="large">Large</Checkbox>

Label and hint

Pass the label as children or the label prop. Add a hint for a secondary line beneath. For a label-less checkbox (e.g. a table “select all”), pass aria-label.

Email notificationsWeekly digest, never marketing.
<Checkbox label="Email notifications" hint="Weekly digest, never marketing." />
<Checkbox aria-label="Select all rows" />

States

Checkboxes are unchecked, checked, or indeterminate (for partial selection in groups).

CheckedUncheckedIndeterminate
<Checkbox defaultChecked label="Checked" />
<Checkbox label="Unchecked" />
<Checkbox defaultChecked="indeterminate" label="Indeterminate" />

Disabled

The disabled attribute dims the checkbox and removes it from the tab order.

DisabledDisabled checked
<Checkbox disabled label="Disabled" />
<Checkbox disabled defaultChecked label="Disabled checked" />

Controlled

Uncontrolled by default: pass defaultChecked and read the value from onStateChange or at form-submit. Make it controlled with checked and onStateChange to drive it from your store.

Omit checked or pass undefined to release control and retain the applied state. Form reset and browser restoration preserve a controlled state. An uncontrolled reset restores defaultChecked and makes later default changes apply until the next interaction or restoration.

const [agreed, setAgreed] = useState(false)
<Checkbox
checked={agreed}
onStateChange={(_e, { next }) => setAgreed(next as boolean)}
>
I agree
</Checkbox>

onStateChange fires before the element applies, with signature (event, { next, prev }). Call event.preventDefault() to veto the change. A controlled checkbox with no onStateChange is read-only.

Events

Three callbacks, in firing order:

Callback When Cancelable Payload
onStateChange(e, { next, prev }) before the toggle applies ✅ e.preventDefault() vetoes next / prev state
onChange(e) after it applies — native change event
onValueChange(e, attrs) after it applies — { checked, indeterminate, name, value }

Reach for onStateChange only to intercept or veto (or to drive a controlled checkbox). For the everyday “react to the new value”, use onValueChange — it hands you the value without touching event.target. onChange is the plain native hook. (In controlled mode onChange/onValueChange fire once you’ve updated checked.) onFocus / onBlur work too — the host is the focusable element.

Checkbox has onStateChange (and Input doesn’t) because its state is discrete — checked · unchecked · indeterminate — so a transition can be vetoed before it applies. A text field’s value is free-form, with no discrete state to model or veto, so Input (and the composed Select) expose only the onValueChange half. The onValueChange shape is identical across all of them.

<Checkbox onValueChange={(_e, { checked }) => save(checked)}>Subscribe</Checkbox>

Indeterminate

Set checked="indeterminate" to show the “mixed” state (minus glyph). Common for a parent checkbox whose state derives from its children: all on = checked, all off = unchecked, mixed = indeterminate.

Parent
Child 1Child 2
function ParentChild() {
const [on, setOn] = useState([true, false])
return (
<>
<Checkbox
checked={on[0] && on[1] ? true : on[0] || on[1] ? 'indeterminate' : false}
onStateChange={(_e, { next }) => setOn([next === true, next === true])}
>
Parent
</Checkbox>
<div style={{ marginInlineStart: 24 }}>
<Checkbox
checked={on[0]}
onStateChange={(_e, { next }) => setOn([next === true, on[1]])}
>
Child 1
</Checkbox>
<Checkbox
checked={on[1]}
onStateChange={(_e, { next }) => setOn([on[0], next === true])}
>
Child 2
</Checkbox>
</div>
</>
)
}

Accessibility

The label names the checkbox for screen readers, and the hint supplies its description. Without a visible label, pass aria-label. Screen readers announce the current state.

Space toggles the checkbox. Follows the WAI-ARIA checkbox pattern.

Component props

Prop Type Default Description
label? string — Visible label — the value of the checkbox (clicked along with the box). Convenience for the common single-string case; for richer content (markup, a link, an info icon) use children. When both are supplied, label renders first. Required unless children or aria-label is provided (a role="checkbox" takes its name from the author, not the markup).
hint? ReactNode — Secondary text rendered under the label and exposed as the checkbox's accessible description. Not part of the accessible name.
checked? CheckboxValue — Controlled checked state. When provided the checkbox is controlled — it renders exactly this and never self-applies; onStateChange is a request the consumer accepts by updating this prop. Use defaultChecked for an uncontrolled checkbox. 'indeterminate' shows the minus glyph and takes visual precedence; clicking it requests true.
defaultChecked? CheckboxValue false Initial checked state for an uncontrolled checkbox. Read once; later changes are ignored and the element updates its state after interaction.
disabled? boolean — Disable the checkbox (no interaction, dropped from the tab order).
round? booleannumberstring — Round the checkbox mark to a circle (border-radius: 999px on the box). Pass a number (px) or a CSS length string for a rounded-square mark instead.
name? string — Form field name. Inside a <form> the checkbox submits under this name, contributing value when checked — like a native checkbox.
value? string "on" Value submitted with the form when checked — like a native checkbox.
tone? neutralbrandcriticalinfosuccesswarningstring 'neutral' Color of the mark. In the default all scope this colors the checked-box fill and unselected box border. A named tone or any literal CSS color ('#ff1493', 'rebeccapurple') for a one-off custom tone. Named tones track light/dark mode automatically; a custom color keeps its hue + chroma and pins lightness to the fill curve. Set toneScope="selected" to tone only the checked mark and leave the empty box neutral. The label + hint stay neutral — recolor them in plain CSS via the theme-aware --text-N-{tone} tokens.
toneScope? allselected 'all' Apply tone to every state, or only while checked so the empty box stays neutral. selected is useful when a tinted resting border would read as a validation state.
size? smallmediumlarge 'medium' Size variant. small=14px, medium=16px, large=18px box.
onStateChange? (event, detail) => void — Fired on click / Space before the element applies any change. Event-first so event.preventDefault() is the synchronous veto (uncontrolled mode); detail carries { next, prev }. In controlled mode the element never self-applies — answer by updating checked, reject by doing nothing.
onChange? (event) => void — Fired after the checked state changes — a native change event (the post-apply counterpart to onStateChange). Not cancelable. For a controlled checkbox this fires once you've updated checked.
onValueChange? (event, attrs) => void — Like onChange, but with a { checked, indeterminate, name, value } snapshot as the second argument, matching Input's onValueChange.
Inherited props (children, className, id, slot, style, tabIndex, title)
Prop Type Default Description
children? ReactNode — Content to render in place of any default content.
className? string — Adds CSS classes to the root element and preserves its existing classes.
id? string — HTML id attribute.
slot? string — Named slot in a parent web component, such as slot="header" in a <Dialog>.
style? CSSProperties — Inline CSS styles on the root element.
tabIndex? number — Tab order. Set to -1 to skip the element when tabbing.
title? string — HTML title attribute, shown as a browser tooltip on hover.

Plus every standard DOM event handler (onClick, onFocus, onKeyDown, …) and any data-* or aria-* attribute — forwarded as-is to the underlying <a-*> element.

Web Component

Use <a-checkbox> directly outside JSX. Set role="checkbox" and tabindex="0" for keyboard access. Its label and hint supply the accessible name and description.

Email updatesProduct and security notices.
<a-checkbox role="checkbox" tabindex="0" name="updates" default-state="checked">
<a-checkbox-label>Email updates</a-checkbox-label>
<a-checkbox-hint>Product and security notices.</a-checkbox-hint>
</a-checkbox>

Native HTML checkbox

For a standard HTML form, add data-anta to a native checkbox. It keeps the browser’s label behavior, checked and indeterminate states, and form submission while using Anta’s control surface.

size, tone, tone-scope, and round use the matching Checkbox visual treatments.

<div style="display: grid; gap: 8px">
<label><input data-anta type="checkbox" name="updates" size="small" tone="brand"> Product updates</label>
<label><input data-anta type="checkbox" name="security" tone="success" tone-scope="selected" checked> Security notices</label>
<label><input data-anta type="checkbox" name="billing" size="large" round tone="warning" checked> Billing alerts</label>
</div>

Styling

Reach for the props first: tone chooses the mark color and toneScope chooses whether it applies in all states or only while selected (any CSS color derives the full rest/hover/active curve in oklch); size controls dimensions + type. To tint the label + hint, add a color rule (on the host for the label, on a-checkbox-hint for the hint) with the --text-N-{tone} tokens — there’s no text-tone prop. The focus ring is the global --focus-ring.

<Checkbox tone="#e0457b" defaultChecked>Custom color</Checkbox>

For anything else, <a-checkbox> is light-DOM — the box is ::before, the check/minus glyph is ::after (a masked SVG — swap --checkbox-mask-check for a custom mark), the label/hint are the a-checkbox-label / a-checkbox-hint children. Target them with plain CSS, per state (an un-layered rule beats @layer anta without !important). The classes below are just for the demos:

OutlinedCustomizedBeautified
/* Outlined — an unfilled box: the checked box keeps its background and paints the
border + checkmark in the tone color (light-dark() tunes both themes at once). */
a-checkbox.outlined:state(checked)::before {
background: var(--checkbox-bg);
border-color: light-dark(#5f4bc3, #7460d7);
}
a-checkbox.outlined:state(checked)::after {
background-color: light-dark(#5f4bc3, #7460d7);
}
/* Customized — bold label, bigger circular box, bigger checkmark.
Colors use light-dark() so they're tuned for both themes at once. */
a-checkbox.customized { font-weight: 700; }
a-checkbox.customized::before,
a-checkbox.customized::after { inline-size: 24px; block-size: 24px; margin-block-start: -2px; }
a-checkbox.customized::before { border-radius: 50%; border-width: 2px; }
a-checkbox.customized::after { mask-size: 16px; } /* bigger mark */
a-checkbox.customized:state(checked)::before {
background: light-dark(#1f6e5f, #2f9c84);
border-color: light-dark(#1f6e5f, #2f9c84);
}
/* Beautified — a full white heart mark swapped in via --checkbox-mask-check
(centered by the default mask-position), sized down a touch, on a pink fill */
a-checkbox.beautified {
--checkbox-mask-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54z'/%3E%3C/svg%3E");
}
a-checkbox.beautified::before { border-radius: 8px; }
a-checkbox.beautified::after { mask-size: 10px; } /* smaller heart */
a-checkbox.beautified:state(checked)::before {
background: light-dark(#ec4899, #f472b6); /* pink, both themes */
border-color: transparent;
}