Diwa Design System

Checkbox

Checkboxes allow users to select one or more options from a set, or toggle a single binary choice. Supports indeterminate state for partial selection patterns.

Keyboard interaction

KeyAction
TabMove focus to the checkbox.
Shift + TabMove focus to the previous focusable element.
SpaceToggle the checkbox. If indeterminate, transitions to checked on first press. No-op when disabled.

Screen reader behaviour

The component uses a native <input type="checkbox"> inside Shadow DOM with delegatesFocus: true. Screen readers announce the correct role automatically without any extra ARIA attributes.

Attribute / PropertyValue / Behaviour
rolecheckbox (implicit from native <input type="checkbox">)
aria-checked"true" when checked, "false" when unchecked. Set to "mixed" automatically when indeterminate={true}.
aria-disabledNot set — native disabled attribute is used instead, which is equivalent for screen readers.
aria-describedbyAutomatically set to the message element ID when state is "error" or "success" and a message is provided.
aria-requiredNot needed — native required attribute is passed to the inner <input>, which conveys the same semantics.

WCAG 2.2 compliance

1.4.3 Contrast (Minimum) — AAPass

Label text uses --diwa-text-primary. Error/success border colours meet 3:1 non-text contrast against backgrounds.

1.4.11 Non-text Contrast — AAPass

The checkbox border (--diwa-border on dark: zinc-700; on light: zinc-300+) has ≥ 3:1 contrast against adjacent surface colours.

2.1.1 Keyboard — APass

All states are reachable and togglable via keyboard alone (Tab + Space).

2.4.7 Focus Visible — AAPass

A tokenized focus outline (var(--diwa-focus-ring-width) solid --diwa-border-focus) is always shown on keyboard focus via :focus-visible.

4.1.2 Name, Role, Value — APass

Native checkbox semantics are preserved; label prop provides the accessible name; indeterminate sets aria-checked="mixed".

1.3.1 Info and Relationships — APass

Error/success messages are linked via aria-describedby so they are announced to screen readers along with the input.

Best practices

  • Always provide a label. The label prop is required for an accessible control. Use hide-label only when a visible label cannot fit the layout — the text is still present in the accessibility tree.
  • Derive indeterminate from state, do not set it imperatively. The component clears the indeterminate attribute on user click. Compute it from your data model (someChecked && !allChecked) and pass it as a prop so it re-derives correctly after each state change.
  • Pair error state with a meaningful message. Setting state="error" alone (without a message prop) changes the border colour but provides no accessible feedback. Always include a human-readable message that is linked via aria-describedby.
  • Disabled ≠ read-only. A disabled checkbox is excluded from form submission and cannot be focused by default. If you need to allow focus (e.g. to show a tooltip explaining why it is unavailable), use custom CSS pointer-events: none instead and manage the visual disabled appearance yourself.