Scale input

For quick self-ratings in the athlete app (wellness check-in, training load) and short feedback questions. Each step is a large tap target; the anchors at both ends say what the extremes mean ("schlecht" … "super").

Underneath it is a radio group: one value, arrow keys to change, submitted with the form. Each radio has an accessible name like "1 von 5, schlecht", so the anchors are only decorative for screen readers.

Use the same direction everywhere: low = bad / easy on the start side, high = good / hard on the end side. Don't pre-select a value unless the person already answered.

Examples

Wellness: sleep

Wie hast du geschlafen?
Data
render({
  "id": "sleep",
  "legend": "Wie hast du geschlafen?",
  "minLabel": "schlecht",
  "maxLabel": "super",
  "value": 4
})
Markup
<fieldset class="fieldset">
<legend>Wie hast du geschlafen?</legend>
<div class="scale">
<span class="scale__anchor" aria-hidden="true">schlecht</span>
<div class="scale__options"><label><input type="radio" id="sleep-1" name="sleep" value="1" aria-label="1 von 5, schlecht"><span>1</span></label><label><input type="radio" id="sleep-2" name="sleep" value="2" aria-label="2 von 5"><span>2</span></label><label><input type="radio" id="sleep-3" name="sleep" value="3" aria-label="3 von 5"><span>3</span></label><label><input type="radio" id="sleep-4" name="sleep" value="4" aria-label="4 von 5" checked><span>4</span></label><label><input type="radio" id="sleep-5" name="sleep" value="5" aria-label="5 von 5, super"><span>5</span></label></div>
<span class="scale__anchor" aria-hidden="true">super</span>
</div>
</fieldset>

Effort (RPE 1–10)

Wie anstrengend war das Training?
Data
render({
  "id": "rpe",
  "legend": "Wie anstrengend war das Training?",
  "max": 10,
  "minLabel": "sehr leicht",
  "maxLabel": "maximal",
  "required": true
})
Markup
<fieldset class="fieldset">
<legend>Wie anstrengend war das Training?</legend>
<div class="scale">
<span class="scale__anchor" aria-hidden="true">sehr leicht</span>
<div class="scale__options" style="--scale-steps: 10"><label><input type="radio" id="rpe-1" name="rpe" value="1" aria-label="1 von 10, sehr leicht" required><span>1</span></label><label><input type="radio" id="rpe-2" name="rpe" value="2" aria-label="2 von 10" required><span>2</span></label><label><input type="radio" id="rpe-3" name="rpe" value="3" aria-label="3 von 10" required><span>3</span></label><label><input type="radio" id="rpe-4" name="rpe" value="4" aria-label="4 von 10" required><span>4</span></label><label><input type="radio" id="rpe-5" name="rpe" value="5" aria-label="5 von 10" required><span>5</span></label><label><input type="radio" id="rpe-6" name="rpe" value="6" aria-label="6 von 10" required><span>6</span></label><label><input type="radio" id="rpe-7" name="rpe" value="7" aria-label="7 von 10" required><span>7</span></label><label><input type="radio" id="rpe-8" name="rpe" value="8" aria-label="8 von 10" required><span>8</span></label><label><input type="radio" id="rpe-9" name="rpe" value="9" aria-label="9 von 10" required><span>9</span></label><label><input type="radio" id="rpe-10" name="rpe" value="10" aria-label="10 von 10, maximal" required><span>10</span></label></div>
<span class="scale__anchor" aria-hidden="true">maximal</span>
</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.

FieldTypeRequiredDefaultDescription
idstringrequired—Id prefix for the option ids {id}-1 … {id}-{max}. Must be unique on the page.
pattern ^[A-Za-z][\w-]*$
legendstringrequired—The question ("Wie hast du geschlafen?").
min 1 chars
namestringoptional—Form field name; defaults to id.
maxintegeroptional5Number of steps; the scale runs from 1 to max.
≥ 3 · ≤ 10
valueintegeroptional—Selected step, if already answered.
≥ 1 · ≤ 10
minLabelstringoptional—Anchor word for 1 ("schlecht", "sehr leicht").
maxLabelstringoptional—Anchor word for max ("super", "maximal").
requiredbooleanoptionalfalseAn answer is required (native required).
JSON Schema
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "legend"
  ],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[A-Za-z][\\w-]*$",
      "description": "Id prefix for the option ids `{id}-1` … `{id}-{max}`. Must be unique on the page."
    },
    "legend": {
      "type": "string",
      "minLength": 1,
      "description": "The question (\"Wie hast du geschlafen?\")."
    },
    "name": {
      "type": "string",
      "description": "Form field name; defaults to `id`."
    },
    "max": {
      "type": "integer",
      "minimum": 3,
      "maximum": 10,
      "default": 5,
      "description": "Number of steps; the scale runs from 1 to `max`."
    },
    "value": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10,
      "description": "Selected step, if already answered."
    },
    "minLabel": {
      "type": "string",
      "description": "Anchor word for 1 (\"schlecht\", \"sehr leicht\")."
    },
    "maxLabel": {
      "type": "string",
      "description": "Anchor word for `max` (\"super\", \"maximal\")."
    },
    "required": {
      "type": "boolean",
      "default": false,
      "description": "An answer is required (native `required`)."
    }
  }
}

Markup & states

Root: div (inside fieldset.fieldset).scale — usable without render() by writing the markup directly.

Attributes

AttributeValuesDefaultDescription
style="--scale-steps: N"integer—Number of columns (set by render when max ≠ 5).
aria-label (on radios)"{n} von {max}[, anchor]"—Accessible name per step.

Parts

PartDescription
.scale__anchorAnchor words at start and end (aria-hidden; the meaning is in the radio names).
.scale__optionsGrid of steps.
.scale__options label > input + spanTransparent radio over the visible number.

States

StateDescription
input:checked + spanAccent fill.
input:focus-visible + spanFocus ring.

CSS custom properties

PropertyDefaultDescription
--scale-steps5Number of grid columns in .scale__options.

Accessibility

  • Native radios in a fieldset with legend: the question is announced, arrow keys move between steps.
  • Each radio is named "3 von 5"; the end steps include the anchor word ("1 von 5, schlecht").
  • Steps are at least 44 px high; on narrow phones scales above 7 steps get tight — prefer 5.

Guidance

DoKeep the same scale and anchors for the same question everywhere, so answers stay comparable.
DoUse 5 steps for wellness, 10 only for established scales like RPE.
Don'tDon't use colour alone (red … green) to mean good/bad.
Don'tDon't pre-select a "neutral" middle value.

Related: Checkbox / radio group · Segmented control