Checkbox

Zero or more choices out of a set, each independent. The indeterminate state is a parent's summary of its children, not a third value.

Accessibility

What Checkbox does for you, and what it leaves to the page around it.

What it renders

A real <input type="checkbox"> inside the <label> that is the component's root. The role, the checked state, Space to toggle and focus all come from the browser.
  • Space toggles and Tab moves on. Each checkbox is its own tab stop, with no arrow-key group.
  • The label text is part of the click target, with no htmlFor and id pair to keep in sync.
  • Do not put a link or button inside the label. It adds a second tab stop, and clicking it also toggles the box.
  • classNames has three keys: root (the label), icon (the box) and label (the text). Styling through them changes no semantics.
  • The check mark is CSS, so there is no icon to name.

The accessible name

The name is the text of the children you pass.
  • <Checkbox /> with no children has no name at all, and nothing warns.
  • title is not a name. It goes on the box, not the input, and only adds a tooltip.
  • Where there is no room for text, like a table's row selector, pass aria-label, which reaches the input. TypeScript does not check hyphenated attributes, so confirm it in the accessibility tree.
  • Name the row, not the verb: “Select invoice 1043”, not “Select”.
  • Everything in an option's label is read as its name. A count or a Tag is fine. Put explanations under the option.

How indeterminate is announced

indeterminate sets the input's mixed state, so a screen reader says “mixed” or “partially checked”.
  • checked is still what the input reports and submits.
  • It is set after the component mounts, so server-rendered HTML does not show it and a snapshot test of the markup will not see it.
  • Mixed only says “some”. If the number matters, put it in the label: “Select all (3 of 8)”.
  • Do not fake it with styling. A box that only looks half checked is announced as unchecked.

Grouping and naming a group

Checkbox.Group renders a <div> with role="group", so the options arrive as one set. What it lacks is a name.
  • Point aria-labelledby at the heading already on screen, or use aria-label when there is none.
  • Inside a Form.Item, point at the field label rather than repeating it.
  • The group gives every option its name but makes none up. Set one if the form submits natively.
  • Keep the option order stable, so a reader tabbing back meets the options in the same order.
  • Unrelated settings do not belong in one group. Give each its own label.

Disabled semantics

  • Tab skips a disabled checkbox, so a keyboard user never hears its label.
  • Disabling the group takes the whole field out of the tab order. Disable only the options that do not apply.
  • Put the reason in text beside the option, not only gray ink.
  • Show a fixed value as text, which stays selectable and in the reading order.

Contrast and motion

  • Check three pairs: the box border against the surface, the check mark against the fill, and the label against the surface. A custom primary color can pass two and fail the mark.
  • The box outline needs 3:1 and the label 4.5:1. The Tokens tab lists the tokens involved.
  • The check mark carries the state, not only color. Keep it if you restyle the box.
  • The focus ring is drawn on the box while the real input is hidden. Remove it and keyboard users see nothing.
  • The check animation and click wave turn off with the theme's motion token.