Introduction

When it comes to validating the content of input fields on the frontend, things are much easier now than they they used to be. We can use the <^>:required<^>, <^>:optional<^>, <^>:valid<^> and <^>:invalid<^> pseudo-classes coupled with HTML5 form validation attributes like <^>required<^> or <^>pattern<^> to create very visually engaging results. These pseudo-classes work for <^>input<^>, <^>textarea<^> and <^>select<^> elements.

Input pseudo-classes

valid illustration for: Input pseudo-classes

Here's an example of the pseudo-classes at work. Let's start with this HTML markup:

				
					
&lt;form action="#"&gt;

 &lt;input type="text" placeholder="First Name" required&gt;

 &lt;input type="email" placeholder="Email" required&gt;

 &lt;input type="text" placeholder="Nickname"&gt;

 &lt;input type="text" placeholder="Favorite pizza topping"&gt;

&lt;/form&gt;

				
			

And let's apply the following styles:

				
					
input {

 border: 2px solid;

 border-radius: 4px;

 font-size: 1rem;

 margin: 0.25rem;

 min-width: 125px;

 padding: 0.5rem;

 transition: background-color 0.5s ease-out;

}

input:optional {

 border-color: gray;

}

input:required {

 border-color: black;

}

input:invalid {

 border-color: red;

}



				
			

Here's the result:

[codepen alligatorio vYGeLYw]

In the demo above, <input type="email" is an HTML5 input type that tells browsers to expect an email address. Because we are also using the required attribute, modern browsers will only set the input to :valid when a valid email is entered.

Adding the :focus pseudo-class

Let's make things even more interesting by also styling according to the focus state and add a background image and color depending on the validity state and only when the input is in focus. We'll use the same HTML markup.

Here's our updated CSS:

				
					
input {

 border: 2px solid;

 border-radius: 4px;

 font-size: 1rem;

 margin: 0.25rem;

 min-width: 125px;

 padding: 0.5rem;

 transition: border-color 0.5s ease-out;

}

input:optional {

 border-color: gray;

}

input:required:valid {

 border-color: green;

}

input:invalid {

 border-color: red;

}

input:required:focus:valid {

 background: url("images/css-styling-form-input-validity-section-1.png") no-repeat 95% 50% lightgreen;

 background-size: 25px;

}

input:focus:invalid {

 background: url("images/css-styling-form-input-validity-section-1.png") no-repeat 95% 50% lightsalmon;

 background-size: 25px;

}

				
			

There are two key changes in the above CSS:

  1. input:required:valid applies a success state only to _required_ inputs. Because technically, optional inputs are always valid.
  1. input:focus:valid' and 'input:focus:invalid apply to inputs only when they are focused.

And here's the result:

[codepen alligatorio gOrGPxP]

You could be tempted to add content instead using <^>::before<^> or <^>::after<^> on the input, but unfortunately that's not possible on input elements. One trick would be to have a sibling <^>span<^> element that has content added depending on the validity of the input. Something like this:

input:focus:invalid + span::before { ... }.