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 developer advocate for HERE Technologies. He focuses on JavaScript, serverless 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