CSS allows for the detection of the status of HTML5 form elements through the use of the pseudo-class selectors :valid and :invalid. For the purposes of demonstration I’ll use the email input to start, as it has built-in validation:

<input type="email" name="email" id="email">

Detecting whether the user has entered information correctly in the field is simple, using an attribute selector combined with the :valid pseudo-class:

input[type=email]:valid {
	/* appearance for valid entry */
}

Changing the appearance of the input is good and fine, but I wanted to take the effect further, and add an error message if the information entered by the user was incorrect. One would think we could chain pseudo-class selectors together:

input[type=email]:invalid:after {
	content: “Error message”;
}

Sadly, we cannot use :after or :before directly on a form input. Like the <img> tag it is a replaced element. All is not yet lost: there is another way.

The technique that follows can be used on any input: let’s change the type attribute to text to keep things interesting. For this example, let’s say we are looking for a user’s first name. In that case, the regular expression we’ll use for pattern will be very simple: we won’t accept numerals, but anything else comprised of at least two upper or lowercase letters will be fine:

<input type="text" name="firstname" id="firstname"  pattern="[^0-9][A-Za-z]{2,20}">

I’ll also add a span immediately after the input, with a title attribute that contains an error message associated with invalid content:

<input type="text" name="firstname" id="firstname" pattern="[^0-9][A-Za-z]{2,20}">
<span title="Must be at least two letters, no numbers"></span>

We want to complete the span with the text of the title attribute. This will also mean that browsers that don’t support this part of CSS won’t see the text, creating a state of graceful degradation.  We’ll make sure it’s the span element immediately after the input by using a sibling selector:

input ~ span:after {
	content: attr(title);
	color: red;
	margin-left: 0.6rem;
}

The default appearance of any generated content in the span will be invisible, via use of opacity:

input ~ span:after {
	content: attr(title);
	color: red;
	margin-left: 0.6rem;
	opacity: 0;
}

… but we’ll change that based on the invalidity of the information entered into the field immediately before it:

input:invalid ~ span:after {
	opacity: 1;
}

Done! But the appearance of the error message is a little sudden and clunky: it shows up just as soon as we type the first letter in the field, potentially distracting and confusing users. We’ll delay and fade in the message by using transition-property, duration and delay:

input ~ span:after {
	content: attr(title);
	color: red;
	margin-left: 0.6rem;
	opacity: 0;
	transition: opacity 2s 2s;
}

You can see the completed effect in the small form at the top of this article.

These techniques do not completely eliminate from forms: you’ll still need the scripting technology for features like AJAX validation (checking if a username is already registered in a database, for example), or to recreate the same effect in older browsers. JavaScript can also be used to customize the browser's own built-in validation error messages, as I show in the next article.

You’ll also need or some other server-side technology to act as a secure, impassable fallback for ultimate validation of user-submitted data. But the techniques demonstrated here continue to do what CSS should: push JavaScript into more advanced and useful areas, rather than being used for simple actions on a page.