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.

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 — Child elements. When provided, replaces the component's default label/content.
className? string — CSS class on the component's root element (merged with the component's own classes). Use it directly for layout and positioning — grid/flex placement, margins, alignment — rather than wrapping the component in a <div>/<span>.
id? string — HTML id attribute.
slot? string — Assigns the element to a named <slot> of a parent web component (e.g. slot="header" inside a <Dialog>, slot="footer" inside a <Dialog>).
style? CSSProperties — Inline styles on the component's root element. Set layout/positioning here (or via className) directly on the component instead of adding a wrapper.
tabIndex? number — Tab order. Set to -1 to skip the element when tabbing.
title? string — HTML title attribute — native 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;
}