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 JavaScript 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 PHP 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.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.