How to target non-empty but invalid input elements with CSS

stefanjudis profile image Stefan Judis Originally published at stefanjudis.com ・2 min read

Today a Twitter thread about forms by Arslan Khalid passed my timeline. The thread included a smart CSS rule that wasn't on my radar so far. It targets invalid but not empty (!) input elements. So, why is that cool? Let's take a step back...

Input elements support multiple HTML attributes that can enable input field validation. These attributes reach from defining an input element's type to enable email validation, pattern to only allow particular characters or required to mark the element as mandatory to fill.

You could have an email field like the following.

<!-- a required email address -->
<input type="email" placeholder="susi@example.com" required>

How could you show that the field element is invalid after someone interacted with the field? CSS offers the :invalid pseudo-class for that use case (at least theoretically).

input:invalid {
  border: 2px solid red;

The catch with this pseudo-class is that it matches input elements even when they're empty and/or the user didn't interact with them (which is a huge bummer). If you're relying on the :invalid pseudo-class to visualize validation state, your users' form editing experience starts with a bunch of red borders – not great! Unfortunately, the :empty pseudo-class isn't a great help either, because it targets elements without children. What could you use instead?

:invalid in CSS matches empty fields

While not being the perfect solution either, Arslan's proposed CSS rule is better than purely relying on :invalid. It uses :invalid in combination with :not(:placeholder-shown). This way, elements only get marked as invalid when the placeholder is not shown (meaning that they're not empty anymore).

input:not(:placeholder-shown):invalid {
  border: 2px solid red;

Input element invalid with placeholder-shown class

If you wonder about browser support, :placeholder-shown is cross-browser supported these days.

Edited: Amy Kapers pointed out that it would be great to combine the selector with :focus to not any validation state while someone is still typing. I agree, that's a great idea. Thanks, Amy!

input:not(:placeholder-shown):not(:focus):invalid {
  border: 2px solid red;

Input example showing invalid state after leaving the input

Also, Koen Cornelis brought up the point that placeholders usually have more disadvantages than advantages. If you don't want to define placeholder values, you can still go with a space to keep the same CSS functionality.

<!-- a required email address with a placeholder space -->
<input type="email" placeholder=" " required>

Here's a quick CodePen to see the selector in action and also have a look at Aslan's thread on great forms. 👋

Posted on by:

stefanjudis profile

Stefan Judis


DevRel at Contentful. In love with web performance, new technologies, and accessibility – all the good stuff ✌️.


Editor guide