Formsstable.switch
Switch
An on/off toggle for a setting that takes effect immediately.
Use a switch only when flipping it changes something right away — notification settings, "Erinnerung am Trainingstag", a module flag. The page saves the change on change and shows a save status; there is no "Speichern" button.
For choices that are submitted with a form (consent, "Ich nehme teil"), use a checkbox in a choice group instead.
The label sits at the start, the switch at the end of the row, so lists of settings line up. An optional hint explains the effect ("Am Morgen jedes Trainingstags um 8:00").
Examples
On
Data
render({
"id": "push",
"label": "Push-Nachrichten",
"checked": true
})Markup
<label class="switch">Push-Nachrichten<input type="checkbox" role="switch" id="push" name="push" checked></label>With hint
Data
render({
"id": "reminder",
"label": "Erinnerung am Trainingstag",
"hint": "Am Morgen jedes Trainingstags um 8:00"
})Markup
<label class="switch"><span class="switch__text"><span class="switch__label" id="reminder-label">Erinnerung am Trainingstag</span><span class="switch__hint" id="reminder-hint">Am Morgen jedes Trainingstags um 8:00</span></span><input type="checkbox" role="switch" id="reminder" name="reminder" aria-labelledby="reminder-label" aria-describedby="reminder-hint"></label>Disabled
Data
render({
"id": "sms",
"label": "SMS bei Trainingsausfall",
"hint": "Nur verfügbar, wenn der Verein SMS gebucht hat.",
"disabled": true
})Markup
<label class="switch"><span class="switch__text"><span class="switch__label" id="sms-label">SMS bei Trainingsausfall</span><span class="switch__hint" id="sms-hint">Nur verfügbar, wenn der Verein SMS gebucht hat.</span></span><input type="checkbox" role="switch" id="sms" name="sms" aria-labelledby="sms-label" aria-describedby="sms-hint" disabled></label>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 of the input; also {id}-hint for the hint line. Must be unique on the page.pattern ^[A-Za-z][\w-]*$ |
label | string | required | — | Name of the setting, phrased as the "on" state ("Push-Nachrichten"). min 1 chars |
name | string | optional | — | Setting key / form field name; defaults to id. |
hint | string | optional | — | Secondary line explaining the effect. |
checked | boolean | optional | false | Current state: on. |
disabled | boolean | optional | false | Can't be changed now (e.g. not permitted); explain why in the hint. |
JSON Schema
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"label"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z][\\w-]*$",
"description": "Id of the input; also `{id}-hint` for the hint line. Must be unique on the page."
},
"label": {
"type": "string",
"minLength": 1,
"description": "Name of the setting, phrased as the \"on\" state (\"Push-Nachrichten\")."
},
"name": {
"type": "string",
"description": "Setting key / form field name; defaults to `id`."
},
"hint": {
"type": "string",
"description": "Secondary line explaining the effect."
},
"checked": {
"type": "boolean",
"default": false,
"description": "Current state: on."
},
"disabled": {
"type": "boolean",
"default": false,
"description": "Can't be changed now (e.g. not permitted); explain why in the hint."
}
}
}Markup & states
Root: label.switch — usable without render() by writing the markup directly.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
role="switch" (on input) | — | — | Announced as "Schalter, ein/aus" instead of a checkbox. |
aria-labelledby / aria-describedby (on input) | "{id}-label" / "{id}-hint" | — | With a hint: the name is the label text only and the hint is the description (not both in the name). |
Parts
| Part | Description |
|---|---|
input[type="checkbox"] | The 44×24 track and its thumb (::after), drawn by CSS. |
.switch__text | Wrapper of label text and hint (only rendered with a hint). |
.switch__label | The label text inside .switch__text, id {id}-label. |
.switch__hint | Secondary line (--text-sm, muted), id {id}-hint. |
States
| State | Description |
|---|---|
input:not(:checked) | Off: --color-border-strong track, --color-surface thumb. |
input:checked | On: --color-accent track, thumb moved to the end (mirrored in RTL). |
input:disabled | Not operable: --color-surface-2 track, disabled label colour. |
:focus-visible | Global focus ring on the track. |
Behaviour & events
Module: (page code)
No built-in behaviour. Listen for change on the input, save the setting immediately (optimistic), show the save status, and revert the switch if saving fails.
Events
| Event | Detail | Description |
|---|---|---|
change | event.target.checked → new state, event.target.name → setting key | Native change event. |
Accessibility
- Native checkbox with
role="switch": Space toggles it; state is announced as on/off. - The whole row is the
<label>, so the label text is the accessible name and the hit area. - State is shown by thumb position and colour, and announced by the role — no on/off text needed.
Guidance
Related: Checkbox / radio group · Save status