SegmentedControl

A few mutually exclusive choices, all visible at once.

import { ... } from '@corndev/ui'

Body: json

HTML

<div class="corn-segmented" role="radiogroup" aria-label="Body type" data-size="md">
  <label class="corn-segmented-option">
    <input class="corn-segmented-input" type="radio" name="body" value="none" />
    <span class="corn-segmented-label">none</span>
  </label>
  <label class="corn-segmented-option">
    <input class="corn-segmented-input" type="radio" name="body" value="json" checked />
    <span class="corn-segmented-label">json</span>
  </label>
  <label class="corn-segmented-option">
    <input class="corn-segmented-input" type="radio" name="body" value="form" />
    <span class="corn-segmented-label">form</span>
  </label>
</div>

Classes

Class
.corn-segmented
.corn-segmented-option
.corn-segmented-input
.corn-segmented-label

Attributes

AttributeValues
data-sizesm|md|lg
data-full-widthpresent or absent

Vanilla JS

Required for behaviour — adds builds the options from data and reports changes with each option’s own value; written out as markup, the radios need no JavaScript.

import { createSegmentedControl } from '@corndev/ui';
import '@corndev/ui/theme';

const el = document.querySelector('#target');
const instance = createSegmentedControl(el, { /* options */ });

Options

OptionTypeDefaultDescription
optionsArray<SegmentedOption|string|number>requiredThe choices: strings, numbers (a number is handed back as a number), or { label, value, disabled } — the same shapes as Combobox and SearchBar.
value*undefinedThe chosen value. Pass it to update() to drive the control from outside.
defaultValue*undefinedInitially chosen. Defaults to the first option.
onChange(value: *) => voidundefinedCalled with the newly chosen value.
size'sm'|'md'|'lg''md'
disabledbooleanfalseDisables every option.
fullWidthbooleanfalseStretch to the container, options sharing it equally. Otherwise as wide as its options.
namestringundefinedThe radios’ name, for a form. Generated if not given.

React

SegmentedControl — from @corndev/ui-react.

The props are the same as the options above, plus className.

For a handful of choices — two to five, a word or two each — that sit above the thing they switch: a request body’s type, a schema’s allOf / oneOf / anyOf, a response pane’s pretty / raw / preview. A dropdown would hide the options, and a column of radios would take most of the panel. Seeing the alternatives is the point.

<SegmentedControl
  aria-label="Request body"
  options={['none', 'json', 'form']}
  value={body}
  onChange={setBody}
/>

options takes strings, numbers, or { label, value, disabled } — the same shapes as Combobox and SearchBar, so a control can move between them without reshaping its data. A number comes back as a number.

It is a radio group

Each option is a real radio input, visually hidden. That is what makes it behave like one:

  • the arrow keys move the selection
  • the whole control is one tab stop
  • a screen reader announces “json, radio, 2 of 3” — one choice among several

A row of buttons with aria-pressed looks the same and says something different: “these toggles happen to exclude each other”, which someone using a screen reader then has to find out by trying them.

The chosen option is marked by a raised surface, a border and a heavier weight, not colour alone, and every option is at least a 24 × 24 target at every size.

Without JavaScript

Written out as markup, it needs no script at all — the radios do their own keyboard work:

<div class="corn-segmented" role="radiogroup" aria-label="Body type" data-size="md">
  <label class="corn-segmented-option">
    <input class="corn-segmented-input" type="radio" name="body" value="none" />
    <span class="corn-segmented-label">none</span>
  </label>
  <label class="corn-segmented-option">
    <input class="corn-segmented-input" type="radio" name="body" value="json" checked />
    <span class="corn-segmented-label">json</span>
  </label>
</div>

createSegmentedControl(el, { options, onChange }) builds the same thing from data.

Options

  • size — sm, md or lg
  • fullWidth — stretch to the container, the options sharing it equally. Otherwise it is as wide as its options.
  • disabled on the control disables every option; { disabled: true } on one option disables just that one.