:user-valid und :user-invalid: Formularvalidierung ohne JavaScript
AI generated
{ }
@
CSS · Formulare · Barrierefreiheit · UX
:user-valid und :user-invalid
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.

15 Min. Lesezeit :user-valid · :user-invalid Formular-UX · Barrierefreiheit

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.

11. FAQ: :user-valid und :user-invalid: Das Wichtigste auf einen Blick

1Was ist der Hauptunterschied zwischen :invalid und :user-invalid?
:invalid prüft nur den aktuellen Gültigkeitsstatus und feuert unabhängig von jeder Interaktion. :user-invalid feuert zusätzlich nur, wenn der Nutzer das Feld bereits bearbeitet oder ein Absenden versucht hat.
2Wann genau gilt ein Feld als bearbeitet für :user-invalid?
Typischerweise, sobald der Nutzer das Feld fokussiert, den Wert geändert und den Fokus danach wieder verlassen hat. Zusätzlich löst ein Absende-Versuch mit ungültigem Feld die Pseudo-Klasse ebenfalls aus.
3Warum sollte ich :invalid nicht direkt für rote Fehlermarkierungen nutzen?
Weil :invalid schon beim Laden der Seite feuert, sobald ein Pflichtfeld leer ist. Das zeigt Fehler an, bevor der Nutzer überhaupt die Chance hatte, das Feld auszufüllen.
4Gibt es ein Gegenstück für gültige Felder?
Ja, :user-valid greift nur bei Feldern, die gültig sind und die der Nutzer bereits bearbeitet hat, ideal für positives Feedback wie einen grünen Haken.
5Bleibt :user-invalid aktiv, wenn ich das fehlerhafte Feld wieder leere?
In den meisten Implementierungen ja, weil die Interaktion bereits stattgefunden hat und nicht rückgängig gemacht wird. Das Feld bleibt bis zur korrekten Eingabe als user-invalid markiert.
6Wie kombiniere ich :user-invalid mit optionalen Feldern?
Mit :optional lässt sich der Selektor gezielt auf freiwillige Felder anwenden oder ausschließen, um dort eine dezentere Rückmeldung als bei required-Feldern zu zeigen.
7Ersetzt :user-invalid komplett die JavaScript-Formularvalidierung?
Für die reine Anzeige-Logik von Fehlerzuständen ja. Asynchrone Prüfungen wie eine serverseitige Verfügbarkeitsprüfung brauchen weiterhin JavaScript, das Ergebnis lässt sich aber trotzdem über setCustomValidity() an die native Validierung anschließen.
8Welche Browser unterstützen :user-valid und :user-invalid?
Alle aktuellen Versionen von Chrome, Firefox und Safari unterstützen beide Pseudo-Klassen bereits vollständig, nachdem die Unterstützung schrittweise über mehrere Jahre ausgerollt wurde.
9Wie sichere ich ältere Browser ohne Unterstützung ab?
Mit @supports selector(:user-invalid) lässt sich prüfen, ob der Browser die Pseudo-Klasse kennt, und für ältere Browser auf :invalid:not(:placeholder-shown) als gröberen, aber funktionalen Fallback zurückfallen.
10Funktioniert :user-invalid auch bei benutzerdefinierten Formularkomponenten?
Nur bei echten HTML-Formularelementen, die an der nativen Constraint-Validierung teilnehmen. Komplett per JavaScript nachgebaute Eingabefelder ohne zugrunde liegendes input-Element unterstützen diese Pseudo-Klassen nicht.