Abschnitt 14 · Lektion 2
Formulare gestalten
Ein Eingabefeld ohne CSS steht in 13,33 Pixeln da, in einer anderen Schrift als die Seite um es herum. Formularelemente sind der Sonderfall im CSS. Der Browser bringt für sie eigene Teile mit, die er selbst zeichnet, und die halten sich an weniger Regeln als der Rest. Was davon heute geht und was nicht, hat sich in den letzten Jahren stark verschoben.
Der Reset, der immer der erste ist
<p class="etikett">So kommt es aus dem Browser</p>
<p>
<label for="name-1">Name</label>
<input type="text" id="name-1" placeholder="Vor- und Nachname">
<label for="anliegen-1">Anliegen</label>
<select id="anliegen-1"><option>Anfrage</option></select>
</p>
<p class="etikett">Und so mit vier Zeilen</p>
<p class="gemacht">
<label for="name-2">Name</label>
<input type="text" id="name-2" placeholder="Vor- und Nachname">
<label for="anliegen-2">Anliegen</label>
<select id="anliegen-2"><option>Anfrage</option></select>
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: Georgia, serif;
font-size: 17px;
line-height: 1.6;
}
.etikett {
margin: 0 0 8px 0;
font-size: 14px;
font-family: system-ui, sans-serif;
color: #52525b;
}
p {
margin: 0 0 32px 0;
}
/* Die Felder erben die Schrift nicht von allein.
Und ihr Innenabstand kommt aus einer Zeit, in der
man Felder mit der Maus traf und nicht mit dem
Finger. */
.gemacht input,
.gemacht select {
padding: 8px 16px;
border: 1px solid #d4d4d8;
border-radius: 8px;
font: inherit;
} input, select und textarea erben keine Schrift. Sie holen sie vom Betriebssystem, so wie der Button in Lektion 14.1, und stehen deshalb in einer anderen Familie und einer anderen Größe als alles um sie herum. font: inherit an alle drei ist die erste Zeile jedes Formular-Stylesheets.
Dazu kommen ein eigener Innenabstand, ein eigener Rahmen und ein Radius. Der Default-Innenabstand ist winzig. Ein Feld, das kaum höher ist als sein Text, trifft man auf dem Telefon nicht.
In Eingabefeldern nehme ich mindestens 16 Pixel Schrift. Ist sie kleiner, zoomt iOS beim Antippen automatisch heran, und die Seite steht danach verschoben da. Gemeint ist das als Hilfe. Vermeiden lässt es sich nur mit einer Schrift, die groß genug ist.
Diese Zahl steht im Auskunftsstreifen. Am oberen Feld stehen font-size: 13.3333px und padding: 1px 2px, und der ganze Kasten misst rund 179 × 21. Am unteren stehen 17px (die Größe der Seite, geerbt) und padding: 8px 16px, Maße rund 254 × 45. Die 13,33 sind die Vorgabe des Browsers, und sie liegen unter der Schwelle, ab der iOS das Feld in Ruhe lässt.
accent-color
<div class="gruppe">
<p><input type="checkbox" id="a" checked> <label for="a">Angehakt</label></p>
<p><input type="radio" id="b" name="r" checked> <label for="b">Ausgewählt</label></p>
<p><input type="range" id="c" value="60"></p>
<p><progress value="60" max="100"></progress></p>
</div>
<p class="hinweis">
Eine einzige Deklaration am Elternteil färbt Haken,
Punkt, Regler und Fortschritt. Vor accent-color war das
ein Kapitel eigener Grafiken, und jede davon sah in
einem anderen Browser falsch aus.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.gruppe {
accent-color: #b91c1c;
}
.gruppe p {
margin: 0 0 12px 0;
}
.hinweis {
max-width: 60ch;
margin-top: 24px;
color: #52525b;
} Checkbox, Radio, Regler und Fortschrittsbalken zeichnet der Browser selbst. Lange ließ sich daran nichts ändern, ohne das echte Element zu verstecken und ein eigenes daneben zu bauen. accent-color färbt alle vier in einer Deklaration.
Die Eigenschaft vererbt sich, du kannst sie also einmal auf body oder auf das Formular setzen. Die passende Farbe für Haken und Umrandung rechnet sich der Browser selbst dazu. Nur Chrome auf Android achtet dabei bisher nicht auf genug Kontrast (Stand Oktober 2026), und eine sehr helle Akzentfarbe ergibt dort einen Haken, den man kaum sieht.
Im Streifen steht sie nicht, dort stehen Layout, Farben und Schrift. Fragen kannst du trotzdem. In der Eingabezeile der Console antwortet getComputedStyle(document.querySelector('input[type=checkbox]')).accentColor im Beispiel mit rgb(185, 28, 28). Setzt du die Zeile testweise an eine andere Stelle im Stylesheet und fragst noch einmal, siehst du die Vererbung als Antwort statt als Behauptung.
Der Fokus muss sichtbar sein
Für Formularfelder gilt dasselbe wie für Buttons, also :focus-visible mit einem deutlichen outline und etwas outline-offset (Lektion 7.3). Wer mit der Tastatur durch ein Formular geht, muss jederzeit sehen, wo er ist.
outline: none ohne Ersatz rutscht leicht durch. Beim Bauen arbeitet man mit der Maus und sieht den Ring dabei nie. Gefällt mir der Standardring nicht, ersetze ich ihn durch einen anderen, weglassen kommt nicht in Frage.
:user-invalid statt :invalid
<p class="etikett">mit :invalid</p>
<p>
<label for="mail-1">E-Mail</label>
<input class="frueh" type="email" id="mail-1" required>
</p>
<p class="etikett">mit :user-invalid</p>
<p>
<label for="mail-2">E-Mail</label>
<input class="spaet" type="email" id="mail-2" required>
</p>
<p class="hinweis">
Beide Felder sind leer und beide sind Pflicht. Das obere
leuchtet schon rot, bevor jemand es angesehen hat. Beim
unteren passiert erst etwas, wenn du etwas Falsches
hineinschreibst und das Feld wieder verlässt.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.etikett {
margin: 0 0 8px 0;
font-size: 14px;
color: #52525b;
}
p {
margin: 0 0 24px 0;
}
input {
width: 280px;
padding: 8px 16px;
border: 2px solid #d4d4d8;
border-radius: 8px;
font: inherit;
}
.frueh:invalid {
border-color: #b91c1c;
border-width: 3px;
background-color: #fef2f2;
}
.spaet:user-invalid {
border-color: #b91c1c;
border-width: 3px;
background-color: #fef2f2;
}
.hinweis {
max-width: 60ch;
color: #52525b;
} :invalid trifft, sobald der Wert nicht passt, und ein leeres Pflichtfeld passt nun einmal nicht. Ein Formular mit :invalid-Styling leuchtet deshalb schon rot, bevor jemand ein Zeichen getippt hat. Das liest sich wie ein Vorwurf für etwas, das man noch gar nicht falsch machen konnte.
:user-invalid wartet, bis der Mensch das Feld wirklich bearbeitet und wieder verlassen hat. Erst dann trifft es. Das Gegenstück heißt :user-valid und darf die grüne Rückmeldung geben.
Den Unterschied siehst du im Streifen, ohne ein Zeichen zu tippen. Am oberen Feld stehen border: 2px solid rgb(185, 28, 28) und background-color: rgb(254, 242, 242), also rot, obwohl noch niemand etwas falsch gemacht hat. Am unteren stehen border: 2px solid rgb(212, 212, 216) und Weiß. Tippst du eine unvollständige Adresse ins untere Feld und klickst daneben, wechselt es die Farbe, und im Streifen stehen dieselben Werte wie oben.
Für die Rückmeldung nehme ich nie nur die Farbe. Menschen mit Rotsehschwäche sehen das Rot zwar, verwechseln es aber leicht mit anderen Farben, und die Barrierefreiheitsrichtlinien verlangen schon auf der untersten Stufe ein zweites Merkmal (WCAG 2.2, Kriterium 1.4.1). Im Beispiel ist das der dickere Rahmen. Ein Text daneben, der sagt, was fehlt, kostet auch nichts.
Der Rest, den man selten braucht und ab und zu doch
::placeholder färbt den Platzhaltertext. Er ist Dekoration, aber zu blass mache ich ihn trotzdem nicht, lesbar soll er bleiben. Ein Ersatz für ein Label ist er nicht. Er verschwindet beim Tippen, und danach weiß niemand mehr, was in das Feld gehörte.
resize: vertical an textarea erlaubt das Größerziehen nach unten und verbietet es zur Seite. Ohne diese Zeile kann jeder das Layout seitlich sprengen. Und irgendwer macht es.
field-sizing: content lässt ein Feld mit seinem Inhalt mitwachsen. Ältere Browser behalten einfach ihre feste Größe.
In der Aufgabe steht ein kurzes Formular noch so da, wie der Browser es liefert.
Zum Mitnehmen
Formularfelder erben keine Schrift, font: inherit holt sie zurück, und unter 16 Pixeln zoomt iOS beim Antippen. accent-color färbt Haken und Regler, :user-invalid meldet erst nach einer Eingabe.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.