Formsstable.segmented
Segmented control
A single choice among 2–4 short, mutually exclusive options, shown side by side.
Use it when the options are few and short and the choice changes the current view or text: the form of address in a reply ("du / ihr / Sie"), "Liste / Kalender", "Woche / Monat". Underneath it is a radio group, so it also works inside forms.
For longer option texts, more than four options or options that need a hint, use a radio choice group instead.
The legend names the choice. In toolbars where the context is obvious it may be visually hidden (hideLegend) — it is still read by screen readers.
Examples
Form of address
Data
render({
"id": "register",
"legend": "Anrede in der Antwort",
"value": "du",
"options": [
{
"value": "du",
"label": "du"
},
{
"value": "ihr",
"label": "ihr"
},
{
"value": "sie",
"label": "Sie"
}
]
})Markup
<fieldset class="fieldset">
<legend id="register-legend">Anrede in der Antwort</legend>
<div class="segmented" role="radiogroup" aria-labelledby="register-legend"><label><input type="radio" id="register-1" name="register" value="du" checked><span>du</span></label><label><input type="radio" id="register-2" name="register" value="ihr"><span>ihr</span></label><label><input type="radio" id="register-3" name="register" value="sie"><span>Sie</span></label></div>
</fieldset>View switch, legend hidden, full width
Data
render({
"id": "view",
"legend": "Ansicht",
"hideLegend": true,
"block": true,
"value": "calendar",
"options": [
{
"value": "list",
"label": "Liste"
},
{
"value": "calendar",
"label": "Kalender"
}
]
})Markup
<fieldset class="fieldset">
<legend id="view-legend" class="u-visually-hidden">Ansicht</legend>
<div class="segmented" role="radiogroup" aria-labelledby="view-legend" data-block><label><input type="radio" id="view-1" name="view" value="list"><span>Liste</span></label><label><input type="radio" id="view-2" name="view" value="calendar" checked><span>Kalender</span></label></div>
</fieldset>Data contract
What render(data) accepts — validated against this JSON Schema in development and tests. Unknown fields are rejected. The same schema is in components.json.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | required | — | Id prefix: {id}-legend and option ids {id}-1, {id}-2, … Must be unique on the page.pattern ^[A-Za-z][\w-]*$ |
legend | string | required | — | What is being chosen ("Anrede in der Antwort"). min 1 chars |
hideLegend | boolean | optional | false | Hide the legend visually (kept for screen readers). |
name | string | optional | — | Form field name of the radios; defaults to id. |
value | string | optional | — | Value of the selected option. Without it, the first option is selected — a segmented control always has a selection. |
block | boolean | optional | false | Stretch to the full width of its container (e.g. on phones). |
options | array of objects | required | — | 2–4 options in display order. min 2 items · max 4 items |
options[].value | string | required | — | Submitted value. |
options[].label | string | required | — | Short visible text (one or two words). min 1 chars · max 20 chars |
JSON Schema
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"legend",
"options"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z][\\w-]*$",
"description": "Id prefix: `{id}-legend` and option ids `{id}-1`, `{id}-2`, … Must be unique on the page."
},
"legend": {
"type": "string",
"minLength": 1,
"description": "What is being chosen (\"Anrede in der Antwort\")."
},
"hideLegend": {
"type": "boolean",
"default": false,
"description": "Hide the legend visually (kept for screen readers)."
},
"name": {
"type": "string",
"description": "Form field name of the radios; defaults to `id`."
},
"value": {
"type": "string",
"description": "Value of the selected option. Without it, the first option is selected — a segmented control always has a selection."
},
"block": {
"type": "boolean",
"default": false,
"description": "Stretch to the full width of its container (e.g. on phones)."
},
"options": {
"type": "array",
"minItems": 2,
"maxItems": 4,
"description": "2–4 options in display order.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"value",
"label"
],
"properties": {
"value": {
"type": "string",
"description": "Submitted value."
},
"label": {
"type": "string",
"minLength": 1,
"maxLength": 20,
"description": "Short visible text (one or two words)."
}
}
}
}
}
}Markup & states
Root: div (inside fieldset.fieldset).segmented — usable without render() by writing the markup directly.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
role="radiogroup" | — | — | Group role, named by the legend (aria-labelledby="{id}-legend"). |
data-block | — | — | Full width. |
Parts
| Part | Description |
|---|---|
label | One segment; wraps the radio and its text. |
input[type="radio"] | Transparent radio covering the segment (keeps native keyboard behaviour). |
span | Visible segment text. |
States
| State | Description |
|---|---|
input:checked + span | Raised surface, full-contrast text. |
input:focus-visible + span | Focus ring on the segment. |
Accessibility
- Native radios: Tab enters the group, arrow keys change the selection.
- The group is named by its legend, even when the legend is visually hidden.
- Segment text is short but complete — no icon-only segments without accessible text.
Guidance
Related: Checkbox / radio group · Tabs