How to target non-empty but invalid input elements with CSS
Tags: css, html
Published: 2020-09-09 10:25:52

---
title: How to target non-empty but invalid input elements with CSS
published: true
description: A clever CSS rule combining :invalid and :placeholder-shown to show validation state of non-empty input elements.
tags: CSS, html
canonical_url: https://www.stefanjudis.com/notes/target-non-empty-but-invalid-input-element-with-css/
---

Today a Twitter thread about forms by [Arslan Khalid](https://twitter.com/thearslankhalid/status/1302143480693743616) 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.

```html
<!-- 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).

```css
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](//videos.ctfassets.net/f20lfrunubsq/1n1a66NG9SVWcidoFX9akd/50849a79b0fb499941a5f0b84664cc83/invalid-first.mp4)

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). 

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

![Input element invalid with placeholder-shown class](//videos.ctfassets.net/f20lfrunubsq/3tqVabrhfgfCpuzpyqjJ2S/da5588aa9055fbd6e551f897ff51dffe/invalid.mp4)

If you wonder about browser support, [`:placeholder-shown` is cross-browser supported these days](https://caniuse.com/css-placeholder-shown). 

**Edited:** [Amy Kapers pointed out](https://twitter.com/Amys_Kapers/status/1303480281249116160?s=20) 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!

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

![Input example showing invalid state after leaving the input](//videos.ctfassets.net/f20lfrunubsq/5JESw58ow2GM1tVIsDexVW/7ea21709e7c8c37c2487bab5f709394e/input-placeholder.mp4)

Also, [Koen Cornelis brought up the point that placeholders usually have more disadvantages than advantages](https://www.nngroup.com/articles/form-design-placeholders/). If you don't want to define placeholder values, you can still go with a space to keep the same CSS functionality.

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

--- 

Here's [a quick CodePen](https://codepen.io/stefanjudis/pen/OJNQxmN) to see the selector in action and also have a look at [Aslan's thread on great forms](https://twitter.com/thearslankhalid/status/1302143480693743616). 👋