Switch

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.

FieldTypeRequiredDefaultDescription
idstringrequired—Id of the input; also {id}-hint for the hint line. Must be unique on the page.
pattern ^[A-Za-z][\w-]*$
labelstringrequired—Name of the setting, phrased as the "on" state ("Push-Nachrichten").
min 1 chars
namestringoptional—Setting key / form field name; defaults to id.
hintstringoptional—Secondary line explaining the effect.
checkedbooleanoptionalfalseCurrent state: on.
disabledbooleanoptionalfalseCan'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

AttributeValuesDefaultDescription
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

PartDescription
input[type="checkbox"]The 44×24 track and its thumb (::after), drawn by CSS.
.switch__textWrapper of label text and hint (only rendered with a hint).
.switch__labelThe label text inside .switch__text, id {id}-label.
.switch__hintSecondary line (--text-sm, muted), id {id}-hint.

States

StateDescription
input:not(:checked)Off: --color-border-strong track, --color-surface thumb.
input:checkedOn: --color-accent track, thumb moved to the end (mirrored in RTL).
input:disabledNot operable: --color-surface-2 track, disabled label colour.
:focus-visibleGlobal 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

EventDetailDescription
changeevent.target.checked → new state, event.target.name → setting keyNative 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

DoSave immediately and show a save status nearby.
DoPhrase the label so "on" is the obvious meaning ("Push-Nachrichten", not "Push-Nachrichten deaktivieren").
Don'tDon't put switches in a form with a submit button — use checkboxes.
Don'tDon't use a switch for choices with more than two states.

Related: Checkbox / radio group · Save status