The invalid :invalid CSS input state
A few weeks ago, I was busy with some fine-tuning for my microblog and got stuck on the invalid :invalid state using CSS. I will tell you how to fix this.
A few weeks ago, I was busy with some fine-tuning for my microblog. At some point, I was looking at my input fields on the website and wondering why they were showing a red underline by default while the form was still empty and not submitted. It was time to look for a way to fix the invalid :invalid
state, which I found in the end. However, I still find it strange that input fields were marked as invalid by default when they were empty. In this blog post, I will share my thought process in finding the correct solution.
Let's start by looking at my initial code for the input field that only accepts email addresses. I wrote an input with the type "email" and styled it with some CSS that has a pseudo :invalid
statement, showing a red underline when the form input is invalid. Upon loading the HTML page, the red underline was already showing without any user interaction. This makes no sense to me. I expected the red underline to appear only when the user has some interaction with the form. However, this behavior is, for some reason, the default behavior. I have included the code I was using below.
<input type="email" required placeholder="email”> input{ border: 0.135rem solid black; &:invalid { border-block-end: 0.135rem solid red; } }
My first solution using :placeholder-shown
After a quick search on the internet, I found a way to address this issue. There is a pseudo-class that checks if a placeholder is displayed to the user. The name of this pseudo-class is :placeholder-shown
. If we include this in our CSS selector, we can somewhat detect if the user has interacted with the input field. A downside of using this pseudo-class is that it doesn’t work properly with form validation on submission.
<input type="email" required placeholder="email”> input{ border: 0.135rem solid var(--input-border-color); &:not(:placeholder-shown):invalid { border-block-end: 1px solid var(--accent-color); } }
The correct solution using :user-invalid
An even better solution is to use the pseudo-class :user-invalid
, which mitigates the need for the :not(:placeholder-shown)
selector. And it even works with the browser form validation on submit.
input{ border: 0.135rem solid black; &:user-invalid { border-block-end: 0.135rem solid red; } }
Final word
This is one of those cases where CSS statements can be confusing. Personally, I expected that the :invalid
pseudo-class would only be triggered on form submission, but it wasn’t. The industry might have come to the same conclusion and fixed it by introducing a new pseudo-element, instead of fixing the way :invalid
is working. They might have valid reasons, as they cannot break the web. However, in my opinion, by creating a new pseudo-selector, they make things only more complicated.
「人生は運のゲームではない。勝ちたいなら、一生懸命働け。」— Life is not a game of luck. If you wanna win, work hard. - Sora, No Game No Life