Skip to content

Checkbox Group

Form InputsSince 0.2.0
import { CheckboxGroup } from '@absoluteui/components/checkbox-group';
import '@absoluteui/components/checkbox-group/style.css';

Using Angular? See Checkbox Group for Angular.

Several related, independent choices. CheckboxGroup renders a <fieldset> and <legend> around native checkboxes that share one field name, with group help text, required and minSelected/maxSelected validation, and one localized validation message for the group. Checkboxes use the same visuals as Checkbox.

  • Choosing any number of options from a short, visible list (roughly 2 to 10).
  • Selections with a minimum or maximum count, such as “pick up to three topics”.
  • Exactly one choice: use Radio Group.
  • Long or searchable lists: use a multi-select Combobox or Listbox.
  • A single yes/no choice: use Checkbox.
import { CheckboxGroup } from '@absoluteui/components/checkbox-group';
const host = document.createElement('div');
document.body.append(host);
CheckboxGroup.create(host, {
label: 'Notifications',
name: 'notify',
description: 'Choose at least one channel.',
required: true,
options: [
{ value: 'email', label: 'Email' },
{ value: 'sms', label: 'SMS', description: 'Carrier rates may apply' },
{ value: 'push', label: 'Push notification' },
],
});
import { CheckboxGroup } from '@absoluteui/components/checkbox-group';
const host = document.createElement('div');
document.body.append(host);
const topics = CheckboxGroup.create(host, {
label: 'Topics',
orientation: 'horizontal',
minSelected: 2,
maxSelected: 3,
options: ['Design', 'Security', 'Performance', 'Accessibility'].map((t) => ({ value: t.toLowerCase(), label: t })),
});
host.addEventListener('aui-change', () => {
host.dataset.count = String(topics.value.length);
});
import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { checkboxGroup: { validationTiming: 'input' } } });
import { CheckboxGroup } from '@absoluteui/components/checkbox-group';
import '@absoluteui/components/checkbox-group/style.css';
const host = document.querySelector<HTMLElement>('#topics');
if (host) {
const topics = CheckboxGroup.create(host, {
label: 'Topics',
maxSelected: 3,
options: [
{ value: 'design', label: 'Design' },
{ value: 'security', label: 'Security' },
{ value: 'performance', label: 'Performance' },
{ value: 'a11y', label: 'Accessibility' },
],
});
host.addEventListener('aui-change', () => {
host.dataset.count = String(topics.value.length);
});
}
import { CheckboxGroup } from '@absoluteui/components/checkbox-group';
const host = document.createElement('div');
document.body.append(host);
CheckboxGroup.create(host, {
label: 'Notifications',
name: 'notify',
required: true,
options: [
{ value: 'email', label: 'Email' },
{ value: 'sms', label: 'SMS' },
],
});
<div class="aui-field aui-checkbox-group" data-aui-control> (consumer host)
<fieldset class="aui-checkbox-group__fieldset" aria-describedby>
<legend class="aui-field__label">Label<span class="aui-field__required" aria-hidden="true">*</span><span class="aui-visually-hidden"> (required)</span></legend>
<div class="aui-field__description"> (optional)
<div class="aui-checkbox-group__options">
<div class="aui-checkbox-group__option"> (per option)
<span class="aui-checkbox-group__box">
<input type="checkbox" class="aui-checkbox-group__input" name value>
<span class="aui-checkbox-group__mark"><svg aria-hidden="true"></span>
</span>
<div class="aui-checkbox-group__text">
<label class="aui-checkbox-group__label" for=input>
<div class="aui-checkbox-group__option-description"> (optional)
</div>
</div>
</div>
<div class="aui-field__error aui-validation-message"> (when shown)
</fieldset>
</div>
CheckboxGroup.create(host: HTMLElement, options: CheckboxGroupOptions): CheckboxGroup

Global configuration key: checkboxGroup (see Configuration).

Option Type Default Description
label string Legend text and accessible name of the group. Required.
hideLabel boolean false Visually hide the legend; it remains the accessible name.
options readonly { value: string; label: string; description?: string; disabled?: boolean }[] Choices; values must be unique. Required.
value readonly string[] "[]" Initially selected values; also restored by reset().
name string Form field name shared by every checkbox.
description string Group help text referenced by the fieldset’s aria-describedby.
required boolean false At least one option must be selected.
minSelected number Minimum selections once any option is selected (integer 1..options.length).
maxSelected number Maximum selections (integer 1..options.length).
orientation 'vertical' | 'horizontal' "vertical" Layout of the options.
disabled boolean false Disables the whole group (fieldset disabled).
validate (value: string[]) => string | null Consumer validation; return a message to mark the selection invalid.
icons Partial<{ checked: IconRef; error: IconRef }> Icon slot overrides.
validationTiming 'blur' | 'input' | 'submit' "blur" When validation messages first appear; afterwards they update live. Globally configurable.
locale string "en-US" Message and number-formatting locale. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host; its children are owned by the component.
fieldset HTMLFieldSetElement The grouping fieldset.
inputs HTMLInputElement[] The native checkboxes, in option order.
value string[] Selected values, in option order.
name string | null Form field name.
label string Legend text.
disabled boolean Group disabled state.
validity FieldValidity { valid, error, message }; computed on every change.
dirty boolean Selection differs from the initial selection.
touched boolean Focus has left the group at least once.
options Readonly<CheckboxGroupOptions> Current instance options.
Method Description
setValue(value: readonly string[]): void Programmatic selection (no events); unknown values throw CHECKBOX_GROUP_OPTIONS.
update(options: Partial<CheckboxGroupOptions>): void Merge and apply options; a new option list keeps selections that still exist.
reportValidity(): boolean Show messages now and return validity.
setCustomError(message: string | null): void External error; cleared when the selection changes.
getFormValue(): string[] Selected values for form serialization.
focus(): void Focus the first enabled checkbox.
reset(): void Restore the initial selection and clear dirty/touched/submitted state.
destroy(): void Remove rendering, listeners, registration, and subscriptions.
Event Detail Bubbles Cancelable Description
aui-change { value: string[]; validity: FieldValidity } Yes No The user toggled an option.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-field aui-checkbox-group Host.
aui-checkbox-group--invalid | --disabled State modifiers on the host.
aui-field__label | __required | __description | __error Shared field chrome (the label is the legend).
aui-checkbox-group__fieldset Grouping fieldset.
aui-checkbox-group__options | __options--horizontal Option list and its horizontal layout.
aui-checkbox-group__option One option row.
aui-checkbox-group__box | __input | __mark Checkbox box, native input, and check mark.
aui-checkbox-group__text | __label | __option-description Option label and help text.
Slot Default icon
checked check
error alert-circle

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-disabled-bg | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-on-primary | --aui-color-primary | --aui-color-surface | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-medium | --aui-icon-size-md | --aui-icon-size-sm | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-sm | --aui-space-1 | --aui-space-2 | --aui-space-4 | --aui-touch-target-min

type CheckboxGroupOrientation = 'vertical' | 'horizontal';
interface CheckboxGroupOption { value: string; label: string; description?: string; disabled?: boolean }
interface CheckboxGroupIcons { checked: IconRef; error: IconRef }
interface CheckboxGroupChangeDetail { value: string[]; validity: FieldValidity }

Pattern: Native <fieldset> + <legend> grouping native checkboxes, each with a <label for> (HTML; WAI-ARIA APG Checkbox).

Key Context Action
Tab / Shift+Tab Any Moves focus through each enabled checkbox in order.
Space Checkbox focused Toggles that option (native).
Element Role
fieldset group (native), named by the legend
input checkbox (native)
check marks and icons none (aria-hidden=true)
Attribute Element Value
aria-describedby fieldset Group help text and visible error message ids
aria-describedby input That option’s help text id, when present
aria-invalid input “true” on every checkbox while the group error is shown (the group role does not support aria-invalid)

Each native input is a tab stop and shows a color-focus-ring outline on :focus-visible.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.4.6 Headings and Labels AA
2.4.7 Focus Visible AA
2.5.3 Label in Name A
3.3.1 Error Identification A
3.3.2 Labels or Instructions A
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • The required asterisk is aria-hidden. Because checkboxes and the group role cannot carry aria-required, a visually hidden “(required)” (localizable message checkboxGroup.required) is part of the legend.
  • Checked state is shown by a check mark, not only by the fill color.
  • Errors appear after focus leaves the group by default, not when moving between options.
  • Announcement of fieldset aria-describedby varies by screen reader; not yet manually verified with NVDA, JAWS, and VoiceOver.

default, hover, focus-visible, checked, disabled (group), disabled (option), required, invalid (message shown).

From Trigger To
any user toggles an option selection changed; aui-change; dirty
any focus leaves the group touched; messages shown (blur timing)
any reportValidity() submitted; messages shown for every timing
invalid (custom error) selection changes custom error cleared
any reset() initial selection; pristine, untouched
  • The required asterisk is aria-hidden. Checkboxes and the group role cannot carry aria-required, so a visually hidden “ (required)” (message checkboxGroup.required) is part of the legend.
  • Checked state is shown by a check mark, not only color.
  • Errors appear after focus leaves the whole group, not when moving between options.
  • Manual screen-reader verification is pending (see Known limitations).

Vertical by default. orientation: 'horizontal' wraps options in a row. Labels and help text wrap; rows are at least touch-target-min tall.

Logical properties and flex layout: boxes are at the inline start and horizontal options flow from the inline start.

Colors come from role tokens only. In forced-colors mode boxes use Canvas/CanvasText, checked uses Highlight with a HighlightText mark, and disabled uses GrayText, with forced-color-adjust: none.

configureAbsoluteUI({ components: { checkboxGroup: { validationTiming: 'input' } } });

Changes apply to existing instances immediately.

Instance options override global and theme defaults. All options are dynamic; changing options, name, or icons rebuilds the option rows.

icons.checked (default check) and icons.error (default alert-circle).

All text uses textContent. Validation messages never include selected values. CSP-safe.

No persistence, network access, or telemetry. With name, each checked native checkbox submits name=value.

Two listeners per instance (delegated change, focusout), a shared document-level pointer tracker, and one configuration subscription. Rendering is linear in the number of options. Target: under 2 ms for 10 options; not yet covered by the performance test.

Short lists, up to about 20 options. Unit-tested with 4 options.

  • An empty optional group is valid even with minSelected (HTML constraint semantics); add required to demand a selection.
  • Required and empty reports value-missing (“Select an option.”), not too-few-selected.
  • Disabled options keep their checked state and are still reported in value and getFormValue(); native form submission omits disabled inputs.
  • update({ options }) drops selections whose values no longer exist.
Code Category Cause
CHECKBOX_GROUP_OPTIONS configuration Missing label, empty or duplicate options, unknown value, invalid minSelected/maxSelected, minSelected > maxSelected, invalid orientation.
Validation (validity.error) validation value-missing, too-few-selected, too-many-selected, custom.

A failed construction leaves no rendering, listeners, or subscriptions behind.

  • Selection-count messages have singular and plural forms only (tooFewSelectedOne/tooFewSelected, tooManySelectedOne/tooManySelected); languages with more plural categories need a custom validate message.
  • Some screen readers announce fieldset descriptions only when entering the group.
  • Screen-reader behavior has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • HTML in labels or the legend.
  • A mixed “select all” row inside the group (compose a Checkbox with the group).
  • Nested groups.