HTML5 Form Validation with Style

This post is more than 2 years old.

Welcome to my third and final (although whenever I say that it typically leads to more posts, diversions, and experiments) on HTML5-based form validation. In the first entry I discussed how you could add simple client-side validation to your forms by just using HTML. In my second entry, I demonstrated how to add JavaScript for deeper, and customized validation. In this final entry, I'll discuss how to employ CSS to visually enhance your forms.

While doing research into this topic, I had a vague idea of the CSS styles that could be used. But I had a difficult time finding an exact specification for the feature itself. Called "CSS Pseudo-Classes", these are styles that reflect a state of a DOM item instead of simple tag or class. I came across an excellent article by Ryan Seddon on form validation where he lists the following pseudo-classes:

  • valid
  • invalid
  • required
  • optional
  • in-range
  • out-of-range
  • read-only
  • read-write

Right away, we can see that there are some interesting options here. I went into this research expecting support for invalid and valid, but the other options surprised me. Using in-range and out-of-range, you can actually apply styles to number text fields that are not within a particular range. I also think it is cool that you can combine required, invalid, and valid to visually represent three separate states for a form field. Let's look at an example.

Here is one of the forms I used in my first example. It contains both a login and registration section.

<h2>Login</h2> <form action="" autocomplete="off"> <label for="username">Username</label> <input id="username" name="username" pattern="[a-zA-Z0-9]{5,}" title="Minmimum 5 letters or numbers." required> <!-- Credit: --> <label for="password">Password</label> <input id="password" name="password" type="password" pattern=".{5,}" title="Minmimum 5 letters or numbers." required> <p/> <input type="submit" class="btn" value="Login"> </form>

<h2>Register</h2> <form action="" autocomplete="off"> <label for="username2">Username</label> <input id="username2" name="username" pattern="[a-zA-Z0-9]{5,}" title="Minmimum 5 letters or numbers." required>

<label for="password2">Password</label> <input id="password2" name="password" type="password" pattern=".{5,}" title="Minmimum 5 letters or numbers." required>

<label for="email">Email</label> <input id="email" name="email" type="email" required>

<label for="url">Homepage</label> <input id="url" name="url" type="url">

<p/> <input type="submit" class="btn" value="Register">


And here's how it renders normally.

Now let's add a bit of CSS:

<style> input:required { border-style:solid; border-width:thick; } input:valid { background-color: #adff2f; } input:invalid { background-color: #f08080; } </style>

I've defined 3 different things here - all using pseudo-classes. The first defines a thick border for required items. The second defines a greenish (it's a color - honest) color for valid items and a reddish color for invalid items. Now look at the same form:

Big difference, right? Now look at it after I've done a bit of editing.

What isn't evident in these screenshots is that Chrome and Firefox both animate these styles. It's a minor animation, pretty subtle, but it works rather well. You can demo this form here:

Just imagine if someone with - you know - actual design sense were to work with this? Now let's look at another example. In the form below, I've got a few simple text fields but also some numeric ones:

<form id="mainForm">

    Not required, nothing special:
    &lt;input type="text" id="field0"&gt;

    Just required:
    &lt;input type="text" id="field1" required&gt;

    &lt;input type="email" id="field2" required&gt;

    &lt;input type="url" id="field3" required&gt;

    &lt;input type="number" id="field4" required&gt;

    Number (Min 0):
    &lt;input type="number" id="field5" required min="0"&gt;

    Number (Max 10):
    &lt;input type="number" id="field6" required max="10"&gt;

    Number (Min 1, Max 10):
    &lt;input type="number" id="field7" required min="1" max="10"&gt;

    Number (Min 1, Max 10, Step 2):
    &lt;input type="number" id="field8" required min="1" max="10" step="2"&gt;


I used the following CSS. Note specifically the in-range and out-of-range pseudo-classes:

<style> input:required { border-style:solid; border-width:thick; } input:valid { background-color: #adff2f; } input:invalid { background-color: #f08080; } input:out-of-range { background-color: #808080; } input:in-range { background-color: #8a2be2; } </style>

In Chrome, this is what you get:

My numeric fields are empty, but marked in-range, which is a bit weird. But look what happens when I intentionally enter a bad value for one of them:

As you can see, it did recognize my number was too low and colored it wrong. All very interesting, but it looks like Firefox doesn't support these two at all:

It looks like Firefox isn't supporting the number type at all so it makes sense for the pseudo-class not to be supported either. Thankfully Firefox supports mission critical features like Canvas, which is obviously more much more important than form fields. (Oops, sorry, I started my old rant again. I'll stop.) It does fall back nicely though so it's certainly not a total loss. For an example of that, we can always rely on IE...

You can demo this version below.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA

Archived Comments

Comment 1 by Phillip Senn posted on 3/23/2012 at 8:35 PM

One wonders if there's a jQuery plugin that handles $('input:number') and styles them appropriately. Maybe jQuery UI. I don't know.

Comment 2 by Raymond Camden posted on 3/23/2012 at 8:39 PM

I think _everything_ I've shown in this series could be pollyfilled (faked ;) easily enough. I'm trying to avoid that and just focus on the specs.

Comment 3 by Phillip Senn posted on 3/23/2012 at 8:43 PM

Ah. I'm thinking of someone reading carefully down through the entire post, paying attention to every jot or tittle and finally reading in the last paragraph: "It looks like Firefox isn't supporting the number type at all."

Comment 4 by Mike posted on 3/24/2012 at 12:56 AM

HTML 5 rocks,

unfortunately a lot of corporate customers are still on IE 8 and bellow and validation fails miserably with IE (sometime even 9)


Comment 5 by Barry Crowley posted on 3/24/2012 at 3:01 AM

Ray, great work as usual. Minor point but there is a typo in your title text. The word minimum is spelled incorrectly as "Minmimum".

Comment 6 by Raymond Camden posted on 3/24/2012 at 5:27 PM

@Barry: Thanks, fixed (well in code - going to edit the blog entry in a sec)

@Mike: But remember - even if every user is using a kick butt browser, you STILL can't rely on this. You must use server-side validation as well. So IE not working isn't a big deal to me. To me, the power here is how easy it is to add the client-side validation.

Comment 7 by Peter Drinnan posted on 7/11/2013 at 7:24 PM

This is like a fresh of breath air. I have seen so many bad and ugly methods for form validation, has almost given up on trying to find something like this. Great work!

Comment 8 by Raymond Camden posted on 7/11/2013 at 7:25 PM

Glad you like it. Web Standards FTW.

Comment 9 by Ritabrata Kar posted on 6/25/2014 at 3:01 PM

i need (add,edit,delete) code in php..................for access that page & change(as admin) my record details.

Comment 10 by Raymond Camden posted on 6/25/2014 at 5:55 PM

Then you should write it.

Comment 11 by David Maggard posted on 6/25/2014 at 7:28 PM

He should get on that.