Validierungsfeedback erst dann zeigen, wenn der Nutzer wirklich etwas getan hat
Die klassischen Pseudo-Klassen :valid und :invalid feuern, sobald ein Formularfeld existiert, oft schon vor dem ersten Tastendruck. Das führt zu roten Rahmen um leere Pflichtfelder direkt beim Laden der Seite. Die neueren Pseudo-Klassen :user-valid und :user-invalid warten dagegen auf echte Nutzerinteraktion und lösen dieses Problem vollständig ohne JavaScript.
Inhaltsverzeichnis
- 1. Das Problem: Fehlerzustand, bevor der Nutzer überhaupt etwas getan hat
- 2. Kurzer Rückblick: was :valid und :invalid überhaupt prüfen
- 3. Was :user-invalid konkret auslöst
- 4. :user-valid als symmetrisches Gegenstück
- 5. Der Unterschied im direkten Vergleich: Zeitpunkt des Feuerns
- 6. Praktisches Beispiel: ein Registrierungsformular ohne verfrühte Fehler
- 7. Kombination mit :required, :optional und dem Platzhalter-Trick
- 8. Der Vergleich zur klassischen JavaScript-Lösung
- 9. Browser-Unterstützung und ein sicherer Fallback
- 10. Zusammenfassung
- 11. FAQ
1. Das Problem: Fehlerzustand, bevor der Nutzer überhaupt etwas getan hat
Ein Formular mit einem als required markierten E-Mail-Feld gilt aus Sicht des Browsers als ungültig, solange es leer ist. Wird dieser Zustand direkt mit der Pseudo-Klasse :invalid gestylt, erscheint der rote Fehler-Rahmen bereits beim Laden der Seite, obwohl der Nutzer noch nicht einmal die Möglichkeit hatte, das Feld auszufüllen. Das wirkt anklagend und verunsichert eher, als zu helfen.
Dieses Verhalten ist kein Bug, sondern folgt exakt der Spezifikation: :invalid beschreibt einen reinen Zustand, unabhängig davon, wie er entstanden ist. Für viele Jahre war der einzige Ausweg, mit JavaScript eigene Klassen erst nach einem blur- oder submit-Event zu setzen und die eigentliche CSS-Pseudo-Klasse zu ignorieren. Genau diese Lücke schließen :user-valid und :user-invalid nativ.
2. Kurzer Rückblick: was :valid und :invalid überhaupt prüfen
Die Pseudo-Klassen :valid und :invalid spiegeln das Ergebnis der HTML-eigenen Constraint-Validierung wider: Sie berücksichtigen Attribute wie required, pattern, min, max, type und minlength/maxlength, ganz ohne dass eigenes JavaScript nötig wäre. Ein Feld vom Typ email mit einem Wert ohne @-Zeichen gilt beispielsweise automatisch als :invalid.
Diese eingebaute Validierung ist mächtig, weil sie deklarativ direkt aus dem Markup entsteht, hat aber genau die eine Schwäche, die im vorherigen Abschnitt beschrieben wurde: Sie unterscheidet nicht zwischen einem Feld, das der Nutzer bereits angefasst hat, und einem, das seit dem Laden der Seite unangetastet geblieben ist. Beide gelten identisch als :invalid.
3. Was :user-invalid konkret auslöst
Die Pseudo-Klasse :user-invalid greift zusätzlich zur Grundbedingung von :invalid nur dann, wenn der Nutzer das Feld bereits signifikant bearbeitet hat. Browser interpretieren das je nach Feldtyp leicht unterschiedlich, im Kern aber übereinstimmend: Ein Textfeld gilt typischerweise als bearbeitet, sobald der Nutzer es fokussiert, etwas eingegeben und den Fokus danach wieder verlassen hat, also ein blur nach einer echten Änderung.
Zusätzlich lösen die meisten Browser :user-invalid auch dann aus, wenn der Nutzer versucht, das Formular abzuschicken, während ein Feld ungültig ist, selbst wenn dieses Feld noch nie fokussiert wurde. Diese zweite Bedingung stellt sicher, dass ein komplett übersehenes Pflichtfeld beim Abschicken trotzdem sichtbar als fehlerhaft markiert wird, statt für immer unbemerkt zu bleiben.
4. :user-valid als symmetrisches Gegenstück
Analog dazu greift :user-valid nur bei Feldern, die gültig sind und die der Nutzer bereits bearbeitet hat. Das ist besonders nützlich, um positives Feedback zu geben, etwa einen grünen Haken neben einem korrekt ausgefüllten Passwortfeld, ohne dass dieser Haken bereits bei einem komplett leeren, technisch potenziell gültigen optionalen Feld erscheint.
Ohne diese Nutzerinteraktions-Bedingung würde :valid bei jedem optionalen, leeren Feld sofort zutreffen, weil ein leeres, nicht als required markiertes Feld per Definition gültig ist. Ein grüner Rahmen um ein Feld, das der Nutzer noch gar nicht gesehen hat, wäre ebenso verwirrend wie der rote Rahmen aus dem einleitenden Beispiel, nur mit umgekehrtem Vorzeichen.
5. Der Unterschied im direkten Vergleich: Zeitpunkt des Feuerns
Der zentrale Unterschied zwischen den beiden Pseudo-Klassen-Paaren liegt ausschließlich im Zeitpunkt, nicht im geprüften Zustand selbst: :invalid prüft nur, ob die Constraint-Validierung gerade fehlschlägt, :user-invalid prüft zusätzlich, ob der Nutzer bereits eine Chance hatte, das zu korrigieren. Ein Feld kann also technisch ungültig und gleichzeitig noch nicht :user-invalid sein, nämlich direkt nach dem Laden der Seite.
Sobald der Nutzer das Feld verlässt, ohne es korrekt auszufüllen, greifen ab diesem Moment beide Pseudo-Klassen gleichzeitig. Löscht der Nutzer danach seine fehlerhafte Eingabe wieder und macht das Feld erneut leer, bleibt :user-invalid in den meisten Implementierungen weiterhin aktiv, weil die Interaktion bereits stattgefunden hat und nicht rückgängig gemacht wird.
6. Praktisches Beispiel: ein Registrierungsformular ohne verfrühte Fehler
Das folgende Beispiel zeigt ein einfaches Registrierungsformular, bei dem ausschließlich :user-invalid und :user-valid für die farbliche Rückmeldung zuständig sind. Beim ersten Laden der Seite erscheint kein einziges Feld rot, selbst wenn beide Felder als required markiert sind und aktuell leer sind.
Erst wenn der Nutzer ein Feld verlässt, ohne es korrekt auszufüllen, oder das Formular mit einem noch leeren Pflichtfeld abzusenden versucht, färbt sich der Rahmen rot und eine begleitende Fehlermeldung wird über :user-invalid ~ .error-text sichtbar gemacht, ganz ohne ein einziges JavaScript-Event-Listener.
input:user-invalid {
border-color: #dc2626;
background-color: #fef2f2;
}
input:user-invalid ~ .error-text {
display: block;
color: #dc2626;
font-size: 0.875rem;
}
input:user-valid {
border-color: #16a34a;
background-color: #f0fdf4;
}
/* Vor jeder Interaktion bleibt das Feld neutral, unabhängig vom Constraint-Status */
input:not(:user-invalid):not(:user-valid) {
border-color: #d1d5db;
}
7. Kombination mit :required, :optional und dem Platzhalter-Trick
Für differenziertere Zustände lässt sich :user-invalid mit :required kombinieren, um Pflichtfelder strenger zu behandeln als optionale, oder mit :optional, um bei freiwilligen Feldern überhaupt keine rote Markierung zu zeigen, selbst wenn ein eingetragenes Format ungültig ist, sondern nur eine dezente Hinweisfarbe.
In Formularen, die zusätzlich einen schwebenden Label-Effekt über :placeholder-shown nutzen, lässt sich dieselbe Selector-Logik mit :not(:placeholder-shown) kombinieren, um sicherzustellen, dass ein Fehlerzustand niemals gleichzeitig mit dem schwebenden Platzhalter-Label sichtbar wird, was optisch sonst zu Überlappungen führen kann.
8. Der Vergleich zur klassischen JavaScript-Lösung
Vor :user-invalid war der übliche Ansatz, bei blur und submit eigene CSS-Klassen wie .touched oder .was-validated per JavaScript zu setzen und die native Constraint-Validierung entweder komplett zu deaktivieren oder parallel eigene Prüf-Funktionen zu schreiben. Das funktionierte, band aber Formularlogik fest an JavaScript, was Formulare in JavaScript-freien Kontexten wie AMP-Seiten oder bei deaktiviertem JavaScript komplett funktionslos machte.
Mit :user-invalid übernimmt der Browser genau diese Interaktions-Erkennung nativ, was nicht nur den JavaScript-Code einspart, sondern auch garantiert konsistent mit der browsereigenen Validierungslogik bleibt. Für die meisten Standardformulare deckt die reine CSS-Lösung heute den kompletten Anwendungsfall ab, JavaScript wird nur noch für asynchrone Prüfungen wie eine serverseitige Verfügbarkeitsprüfung eines Nutzernamens gebraucht.
9. Browser-Unterstützung und ein sicherer Fallback
Sowohl :user-valid als auch :user-invalid werden inzwischen von allen aktuellen Versionen von Chrome, Firefox und Safari unterstützt, nachdem die Unterstützung über mehrere Jahre schrittweise ausgerollt wurde. Für ältere Browser-Versionen, die diese Pseudo-Klassen noch nicht kennen, bleibt der Selektor im CSS einfach wirkungslos, ohne einen Fehler zu verursachen, was für ein risikoarmes Progressive Enhancement sorgt.
Wer zusätzliche Absicherung für ältere Browser möchte, kombiniert :user-invalid als bevorzugten Selektor mit einer bewussten Feature-Detection über @supports selector(:user-invalid), sodass ältere Browser stattdessen auf eine etwas gröbere, aber immer noch funktionale Lösung mit :invalid:not(:placeholder-shown) zurückfallen.
| Pseudo-Klasse | Feuert bei | Feuert vor Interaktion |
|---|---|---|
:invalid |
Constraint-Validierung schlägt fehl | Ja, sofort beim Laden |
:valid |
Constraint-Validierung erfüllt | Ja, sofort beim Laden |
:user-invalid |
Ungültig UND bereits bearbeitet oder Absenden versucht | Nein |
:user-valid |
Gültig UND bereits bearbeitet | Nein |
:invalid:not(:placeholder-shown) |
Ungültig UND Feld nicht mehr leer | Nein, aber ungenauer als :user-invalid |
Mironsoft
Modernes CSS, Layout-Architektur und Rendering-Performance
CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?
Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.
CSS-Audit
Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.
Architektur-Refactoring
Cascade Layers, Custom Properties und Design Tokens sauber einführen.
Performance-Tuning
Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.
10. Zusammenfassung
:user-valid und :user-invalid: Das Wichtigste auf einen Blick
Kernproblem gelöst
:invalid feuert sofort beim Laden, :user-invalid wartet auf echte Nutzerinteraktion oder einen Absende-Versuch.
Zwei Auslöser
:user-invalid greift nach blur mit Änderung oder wenn der Nutzer trotz ungültigem Feld abzusenden versucht.
Kein JavaScript nötig
Ersetzt die frühere Praxis, eigene touched-Klassen per blur/submit-Listener zu setzen, vollständig deklarativ.
Breiter Browser-Support
Alle aktuellen Versionen von Chrome, Firefox und Safari unterstützen beide Pseudo-Klassen bereits.