The :user-valid and :user-invalid pseudo-classes

Published: November 8, 2023

User input is one of the most sensitive concerns in any user interface. A usable application must help users see, understand, and fix any mistakes in their input. The :user-valid and :user-invalid pseudo-class selectors help improve the user experience of input validation by giving feedback about mistakes only after a user has changed input. With these new selectors, there's no longer a need to write stateful code to keep track of input a user has changed.

The user interaction pseudo-class selectors

The :user-valid and :user-invalid pseudo-class selectors are similar to the existing :valid and :invalid pseudo-classes. Both match a form control based on whether its current value satisfies its validation constraints. However, the advantage of the new :user-valid and :user-invalid pseudo-classes is that they match a form control only after a user has significantly interacted with the input.

A form control that is required and empty will match :invalid even if a user has not started interacting with the page. However, that same form control won't match :user-invalid until the user has changed the input and left it in an invalid state.

Use the :user-valid and :user-invalid pseudo-classes

Use these pseudo-classes to style input, select, and textarea controls, as shown in the following examples:

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

input:user-invalid,
select:user-invalid,
textarea:user-invalid {
  border-color: red;
}
<input required="required" />

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

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

An image that combines 3 screenshots side by side for comparison. Each screenshot shows a web form with the same input, select, and textarea controls. The first screenshot shows the form in its initial state before any user input. The control borders are gray and the help text below explains that each control will currently match the :invalid pseudo-class selector. The second screenshot shows the same form after a user has provided input for each control. The control borders are green and the help text below explains that each control will currently match both the :valid and :user-valid pseudo-class selectors. The third and final screenshot shows the same form after a user has removed all of their input. The control borders are red and the help text below explains that each control will currently match both the :invalid and :user-invalid pseudo-class selectors.

The selectors match based on a combination of user interactions and validation constraints. Interact with the following demo to see them in action:

Better user experience with less code

Without these pseudo-classes, achieving the user experience enabled by :user-valid and :user-invalid required writing extra stateful code. That code needed to keep track of the initial value, the current focus state of the input, the extent of the user's changes to the value, run an extra validity check, and finally add a class to select for styling. You can now rely on the browser to handle all of this automatically.

More resources

Cover photo by Behzad Ghaffarian on Unsplash.