Validation feedback that waits for the user

Form validation is a delicate part of interface design. Show errors too early and the page feels hostile; show them too late and users can't connect the message to their action. The :user-valid and :user-invalid pseudo-classes address this by tying validation styling to actual user interaction with the field.

These selectors work like the existing :valid and :invalid pseudo-classes—both match form controls when their current value satisfies or violates validation constraints. The difference is timing. A required field that is empty matches :invalid the moment the page loads, before a user has touched anything. With :user-invalid, the same control won't match until the user has changed its value and left it in an invalid state.

Styling inputs after interaction

Apply the pseudo-classes to input, select, and textarea controls in stylesheets:

<input required="required" />

<select required="required">
  <option value="">Choose an option</option>
  <option value="1">One</option>
</select>

<textarea required="required"></textarea>

For controls that follow an input element, adjacent sibling selectors can provide contextual feedback:

input:user-valid,
select:user-valid,
textarea:user-valid {
  border-color: green;
}

input:user-invalid,
select:user-invalid,
textarea:user-invalid {
  border-color: red;
}

The matching logic depends on both the user's interaction history and the field's validation rules. As the user fills out a form, :user-valid and :user-invalid reflect the state of each control only after it has been edited. This gives you a straightforward way to style fields based on the current state of interaction, such as highlighting a valid email address in green once it has been entered correctly.

Replacing stateful validation code

Before these pseudo-classes, achieving this behavior required significant script overhead. Developers had to track the input's initial value, monitor its focus state, detect how much of the value the user had modified, run manual validity checks, and apply classes to toggle styles. All of that bookkeeping is now handled by the browser, which knows when a control has been changed and can apply the appropriate pseudo-class automatically.