Styling Form Inputs in CSS With :required, :optional, :valid and invalid

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.

Here’s a basic example of the pseudo-classes at work. Let’s start with this basic markup:

<form action="#" class="simple-form">
  <input type="text" placeholder="First Name" required>
  <input type="email" placeholder="Email" required>
  <input type="text" placeholder="Nick name">
  <input type="text" placeholder="Favorite pizza toping">
</form>

And let’s apply the following styles:

.simple-form {
  display: flex;
  overflow-y: scroll;
  padding-bottom: 1.25rem;
}
.simple-form input {
  margin: 0 .25rem;
  min-width: 125px;
  border: 1px solid #eee;
  border-left: 3px solid;
  border-radius: 5px;
  transition: border-color .5s ease-out;
}
.simple-form input:optional {
  border-left-color: #999;
}
.simple-form input:required {
  border-left-color: palegreen;
}
.simple-form input:invalid {
  border-left-color: salmon;
}

Here's the result:


Notice how our input of type email requires more than just any text to become valid. That's thanks to modern browsers having a different rule to satisfy the required attribute for email input types.

Focus Pseudo-Class

Let’s make things even more interesting by also styling according to the focus state and add a small background image depending on the validity state and only when the input is in focus. We’ll start with the same form markup.

Here’s our styles this time:

.simple-form {
  display: flex;
  overflow-y: scroll;
  padding-bottom: 1.25rem;
}
.simple-form input {
  margin: 0 .25rem;
  min-width: 125px;
  border: 1px solid #eee;
  border-left: 3px solid;
  border-radius: 5px;
  transition: border-color .5s ease-out;
}
.simple-form input:optional {
  border-left-color: #999;
}
.simple-form input:required:valid {
  border-left-color: palegreen;
}
.simple-form input:invalid {
  border-left-color: salmon;
}
.simple-form input:required:focus:valid {
  background: url("/images/check.svg") no-repeat 95% 50%;
  background-size: 25px;
}
.simple-form input:focus:invalid {
  background: url("/images/tnt.svg") no-repeat 95% 50%;
  background-size: 45px;
}

And here's the result:


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 { ... }.


Browser Support

Can I Use form-validation? Data on support for the object-fit feature across the major browsers from caniuse.com.

✖ Clear

🕵 Search Results

🔎 Searching...