Published at
Updated at
Reading time

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="" 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. This is a huge bummer and hopefully the :user-invalid pseudo class gains some better support to solve this issue soon.

MDN Compat Data (source)
Browser support info for :user-invalid

Today, 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.

Was this post helpful?
Yes? Cool! You might want to check out Web Weekly for more WebDev shenanigans. The last edition went out 14 days ago.
Stefan standing in the park in front of a green background

About Stefan Judis

Frontend nerd with over ten years of experience, freelance dev, "Today I Learned" blogger, conference speaker, and Open Source maintainer.

Related Topics

Related Articles