CheckboxGroup

CheckboxGroup makes one field from a set of Checkbox.Root boxes. The value of the group is an array of the checked values. The group also gives each box the same name for the form, and it can disable all of the boxes together.

Anatomy

CheckboxGroup.Root contains CheckboxGroup.Item boxes. That part is Checkbox.Root under the namespace of the group, and the two names are the same component. Each box must have a unique value. The group holds the checked state of each box.

CheckboxGroup.Label names the group. It is optional: aria-label on the root does the same for a name that the user does not see.

<script>
	import { CheckboxGroup } from '@human-kit/ui';
</script>

<CheckboxGroup.Root name="colors" defaultValue={['red']}>
	<CheckboxGroup.Label>Colors</CheckboxGroup.Label>
	<CheckboxGroup.Item value="red">
		<CheckboxGroup.Indicator>x</CheckboxGroup.Indicator>
	</CheckboxGroup.Item>
	<CheckboxGroup.Item value="green">
		<CheckboxGroup.Indicator>x</CheckboxGroup.Indicator>
	</CheckboxGroup.Item>
</CheckboxGroup.Root>

Value

Use bind:value to give the group your state. Use value with onChange to hold the state yourself. There is no prop to declare which of the two you use: value is the source of truth in both, and onChange always reports the change.

A parent that holds value and refuses a change still sees the group move. The group goes back to the value of the parent at the next render. Thus you refuse a change with a render, and not with silence.

Use defaultValue when the group holds its own state.

Forms

Give the group a name. Each box then puts its own value on a hidden input with that name. The form sends one entry for each checked box, which is the behavior of a native group of checkboxes.

The group also sends disabled and readonly down to each box. It keeps required for itself, because native required on a box demands that one box. The group gives data-required for your styles, and it does not enforce a minimum count yet.

Select all

Read allSelected and someSelected from the context of the group. They give a parent checkbox its checked state and its indeterminate state. Call selectAll or clearAll from that parent checkbox.

Keep the parent checkbox out of the group. In the group, it becomes one more value of the group.

<script>
	let group = $state();
</script>

<Checkbox.Root
	checked={Boolean(group?.allSelected)}
	indeterminate={Boolean(group?.someSelected)}
	controlledChecked
	controlledIndeterminate
	onCheckedChange={(checked) => (checked ? group.selectAll() : group.clearAll())}
/>

<CheckboxGroup.Root bind:context={group} aria-label="Colors">
	<!-- boxes -->
</CheckboxGroup.Root>

Usage guidelines

  • Each box in the group must have a unique value. The default value of a box is on, thus two boxes without a value collide.
  • A box in a group ignores its own checked and defaultChecked props. The group holds that state.
  • A box that becomes disabled keeps its place in the value. A checked box that is disabled is a correct state, and the group does not remove it.
  • A box that leaves a mounted group leaves the value, and the group reports the shorter value.
  • The values are strings. Each box puts its value on a native input, which keeps it as text.

Accessibility

  • CheckboxGroup.Root has role="group".
  • Give the group an accessible name with CheckboxGroup.Label, or with aria-label on the root. The label writes aria-labelledby on the group. An aria-labelledby that you give stands, thus your own element wins.
  • CheckboxGroup.Label renders a <span>, and not a <label>. A <label> names one control, thus it cannot name a set.
  • Each box keeps its own tab stop. The Space key changes the box that has the focus.
  • required marks no element with aria-required: role="group" does not support that property. Put the word in the group label, and give the reason with aria-describedby.
  • The group adds no arrow keys. React Aria and Base UI do the same, and the APG agrees. Arrow keys and one tab stop for a full set are the behavior of a group of radio buttons.

API reference

Root

The group container. It makes a div with role="group". It holds the checked state of the boxes in it.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-checkbox-group-root Identifies the group element.
data-disabled Present when the whole group is disabled.
data-orientation The group orientation ("horizontal" or "vertical").
data-readonly Present when the whole group is read only.
data-required Present when the group is required.

Label

The accessible name of the group. It gives its id to the group as `aria-labelledby`.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-checkbox-group-label Present on the label of the group.

Item

One box of the group. It is Checkbox.Root under the namespace of the group, and the two names are the same component.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-checkbox-input
data-checkbox-root Identifies the checkbox element.
data-checked Present when the box is checked.
data-disabled Present when the box or its group is disabled.
data-focus-visible Present when the focus came from the keyboard.
data-focused Present when the box has the focus.
data-indeterminate Present when the box is in the mixed state.
data-pressed Present while the box is held down.
data-readonly Present when the box or its group is read only.
data-required Present when the box is required.
data-unchecked Present when the box is not checked.

Indicator

The mark of a checked box. It is Checkbox.Indicator under the namespace of the group.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-checkbox-indicator Identifies the indicator element.
data-checked Present when the box is checked.
data-disabled Present when the box or its group is disabled.
data-focus-visible Present when the focus came from the keyboard.
data-focused Present when the box has the focus.
data-indeterminate Present when the box is in the mixed state.
data-pressed Present while the box is held down.
data-readonly Present when the box or its group is read only.
data-required Present when the box is required.
data-unchecked Present when the box is not checked.