MutuallyExclusive
A container that enforces a "zero or one" selection rule across a group of checkboxes — when the user checks one option, the previously checked option is unchecked, but unlike a radio group the user is also free to uncheck the current selection and leave the group with nothing selected.
This sits in the gap between RadioGroup (always exactly one) and CheckboxGroup (any number, including all). It is the right choice for optional preferences ("which one of these would you prefer, if any?") rather than required choices. The component renders role="group" with an accessible label so screen readers announce the constraint, and each child checkbox keeps its own native semantics.
Example
Show demo markup
<div class="mutually-exclusive" aria-label="a container ensuring only one option can be selected from a group">MutuallyExclusive</div>
Usage example
<MutuallyExclusive label="Preferred contact method" value={contact} onChange={setContact}>
<CheckboxInput value="email" label="Email" />
<CheckboxInput value="sms" label="SMS" />
<CheckboxInput value="post" label="Post" />
</MutuallyExclusive>
Show Svelte source
// In your Svelte component:
import MutuallyExclusive from "lily-design-system-svelte-headless/components/MutuallyExclusive/MutuallyExclusive.svelte";
<MutuallyExclusive>
<!-- content -->
</MutuallyExclusive>
Usage
<MutuallyExclusive label="Preferred contact method" value={contact} onChange={setContact}>
<CheckboxInput value="email" label="Email" />
<CheckboxInput value="sms" label="SMS" />
<CheckboxInput value="post" label="Post" />
</MutuallyExclusive>
Props
| Prop | Type | Default | Description |
label | string (required) | — | Accessible label for the group |
value | string | null | null | Currently selected option's value, or null when none is selected; bindable |
onChange | (value: string | null) => void | — | Fired when the selected value changes |
children | slot (required) | — | The CheckboxInput controls that make up the group |
...restProps | HTML attributes | — | Spread onto the root <div> |
Keyboard Interactions
- Tab moves focus through the child controls
- Space toggles the focused checkbox (native behaviour); the component handler then unchecks any other previously-checked option
ARIA
role="group" + aria-label identifies the group
- Each child checkbox retains its own native semantics
- Consumers may also use
aria-describedby to reference an explanation of the constraint
When to Use
- When a question allows zero or one answer (radio group requires at least one) and you want a familiar checkbox affordance
- Surveys or settings where "none of these" is a valid state
When Not to Use
- Use
RadioGroup when at least one option must always be selected
- Use
CheckboxGroup when multiple selections are allowed
- Use
SegmentGroup for short, side-by-side mutually exclusive options
Headless
This headless component renders semantic HTML with appropriate ARIA wiring. The consumer provides all visual styling — no CSS, animations, or layout assumptions are baked in.
Styles
The component renders with .mutually-exclusive as the root class. No default styles are included.
Related components
radio-group — mandatory mutually exclusive selection
checkbox-group — multiple selections allowed
segment-group — segmented control for short option lists
References
Implementation Notes
- Wraps a group of selectable controls (typically checkboxes) and enforces that only one can be checked at a time
- When the user checks one option, the previously-checked option is unchecked
- Behaves like a radio group but allows the user to uncheck the current selection (to leave the group with no selection)
- Renders
role="group" with an aria-label so the constraint is communicated to assistive technology
- Children are passed via the
children slot; the consumer composes the individual CheckboxInput controls
Canonical contract
Generated from this component's canonical metadata — the machine-checked source the implementations are held to.
Metadata
- Component: mutually-exclusive
- PascalCase: MutuallyExclusive
- Description: a container ensuring only one option can be selected from a group
- Status: beta — implemented and unit-tested in all seven frameworks with an axe-clean demo page; not yet exercised in composed flows
- HTML tag: <div>
- CSS class: .mutually-exclusive
- Interactive: yes
ARIA
role="group" + aria-label identifies the group - Each child checkbox retains its own native semantics
- Consumers may also use
aria-describedby to reference an explanation of the constraint
Keyboard
- Tab moves focus through the child controls
- Space toggles the focused checkbox (native behaviour); the component handler then unchecks any other previously-checked option
Props
label: string (required) (default: —) — Accessible label for the group value: string | null (default: null) — Currently selected option's value, or null when none is selected; bindable onChange: (value: string | null) => void (default: —) — Fired when the selected value changes children: slot (required) (default: —) — The CheckboxInput controls that make up the group ...restProps: HTML attributes (default: —) — Spread onto the root <div>