Selection groups

Which of radio, checkbox and switch groups to use, how to lay out a group, and how to mark required questions.

Tariff
Pick one.
Notify me by
Pick any.
Features
Each applies at once.

A selection group lets a person choose among a fixed set of options. Spark has three: RadioGroup (exactly one), CheckboxGroup (any number, saved together) and SwitchGroup (any number, each applied at once). All three take the same listStyle and the same SelectionItem rows, so they look alike in a form.

import { CheckboxGroup } from '@epilot/spark-ui/checkbox-group'
import { Checkbox } from '@epilot/spark-ui/checkbox'
import {
  FieldDescription,
  FieldError,
  FieldLegend,
  FieldSet,
} from '@epilot/spark-ui/field'
import { SelectionItem } from '@epilot/spark-ui/selection-item'

Which group when

NeedUse
Exactly one option, saved with the formRadioGroup
Any number of options, saved with the formCheckboxGroup
Any number of settings, each takes effect when flippedSwitchGroup
One option, shown as large cardsRadioCardGroup
One option from a short list of viewsSegmentedControl

A single yes/no question is a lone Checkbox or Switch, not a group. Use a switch when the change applies immediately, a checkbox when the person confirms with a button.

Slot order

Build every group the same way, from top to bottom:

  1. FieldLegend: the question.
  2. FieldDescription: the hint.
  3. FieldError: shown only while the group is invalid.
  4. The options.
<FieldSet indicator="required">
  <FieldLegend>Notify me by</FieldLegend>
  <FieldDescription>Pick any.</FieldDescription>
  {invalid && <FieldError>Choose at least one channel.</FieldError>}
  <CheckboxGroup listStyle="surface" value={value} onValueChange={setValue}>
    <SelectionItem
      control={<Checkbox value="email" />}
      label="Email"
      description="Invoices and reminders."
    />
  </CheckboxGroup>
</FieldSet>

The error renders as a callout between the hint and the options, so the hint stays readable and the error sits next to what it describes.

List styles

listStyle reuses the input chrome (soft, surface, outlined, ghost), so a group matches the inputs around it. Use the switcher above to compare them. Without listStyle the group is a plain stack of controls, as before. SelectionItem is the row that holds the control, label and description; descriptions work with or without listStyle.

Required and optional

  • Use one marking convention per form. Do not mix * and "(optional)".
  • Mark the minority: when most questions are required, mark the optional ones with indicator="optional"; when most are optional, mark the required ones with indicator="required".
  • When you use *, add a "* Required" key line to the form so the symbol is explained.
  • A mark on single inputs is not part of Spark yet; indicator applies to a FieldSet legend.

Error state

Give the FieldError inside the set. The legend turns red, the callout appears above the options and the list takes the error tint. Submit the form below without choosing an option.

Notify me by required
Pick any.

* Required

Accessibility

  • Every group is named by its FieldLegend through the <fieldset>. Do not leave a group without a legend or aria-label.
  • The <fieldset> lists the error, then the hint, in aria-describedby. The error also has role="alert", so it is read when it appears.
  • indicator="required" adds hidden "required" text next to the visible *. indicator="optional" is visible text. Translate either with indicatorLabel.
  • Each SelectionItem names its control by the label and describes it by the description, so a screen reader reads the two apart.
  • Color is never the only signal for an error: the callout states the problem in words.

On this page