How to target non-empty but invalid input elements with CSS
- Published at
- Updated at
- Reading time
- 3min
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. This is a huge bummer and hopefully the :user-invalid
pseudo class gains some better support to solve this issue soon.
119 | 119 | 119 | 88 | 88 | 16.5 | 16.5 | 25.0 | 119 |
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?
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;
}
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;
}
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.
Join 5.4k readers and learn something new every week with Web Weekly.