/* Only show invalid ring while not focused */
input:not(:focus):not(:placeholder-shown):invalid {
border-color: var(--color-invalid);
}
input:not(:focus):not(:placeholder-shown):invalid ~ .error-message {
display: block;
}
/* Only show valid ring while not focused and if a value is entered */
/* :empty won't work here as that targets elements that have no childeren. Therefore we abuse :placeholder-shown */
input:not(:focus):not(:placeholder-shown):valid {
border-color: var(--color-valid);
}
Pulling this straight from the Weekly Platform News, where Šime Vidas covers two long ongoing issues with using :invalid
to style form input validation. Apparently, what happens is…
- [inputs] become
:invalid
while the user is still typing the value.- If a form field is required (
), it will become
:invalid
immediately on page load.Both of these behaviors are potentially confusing (and annoying), so websites cannot rely solely on the
:invalid
selector to indicate that a value entered by the user is not valid. However, there is the option to combine:invalid
with:not(:focus)
and even:not(:placeholder-shown)
to ensure that the page’s “invalid” styles do not apply to the<input>
until the user has finished entering the value and moved focus to another element.
Ryan Florence sums this up nicely in a tweet:
I prefer when forms wait for blur before freaking out 🤨 pic.twitter.com/aLKVovpCao
— Ryan Florence (@ryanflorence) January 27, 2021
So, what this snippet does is enhance :invalid
by combining it with :not(:focus)
and :not(:placeholder-shown)
.
What does that mean? Let’s translate the code into something more readable.
input:not(:focus):not(:placeholder-shown):invalid {}
If an input is not in focus, its placeholder text isn’t shown, and the entered text is invalid… then you can use these styles.
In other words, this prevents the invalid style from being applied until text is entered and focus moves to another element.
input:not(:focus):not(:placeholder-shown):invalid ~ .error-message
Hey, let’s display the error message if those same conditions are met.
This is the exact same thing as above, but selects an .error-message
class and sets it from display: none
to display: block
only after the text is entered and focus moves from the input to something else.
input:not(:focus):not(:placeholder-shown):valid
Oh, the text that’s entered is valid? OK, let’s apply these styles instead.
Again, the same sorta condition, but chained to :valid
instead of :invalid
. That way, we can give the user a different set of styles to indicate that what they typed is good to go!
According to Šime, this sort of snippet might be unnecessary in the future as a greater push is being made for :user-invalid
and :user-valid
. Firefox already intends to ship its un-prefixed solution. Tickets are open to do the same in Safari and Chrome.
Does support all the browsers?
Yeah, it’s got great coverage!
:placeholder-shown
is the “least” supported feature in the set, and that has 95% support coverage. IE 6-9 is the most notable exception.Is there a reason the input tag isn’t inside paragraph tags or similar?
It’s probably that a paragraph isn’t really needed and it’s just a demo. An actual implementation would likely put the input into a proper
<form>
and include labeling and such.Personally, I prefer validation as I type. I feel there are two problems with validation after the control loses focus, particularly with long forms.
Not everyone fills out a form in order so if the last field has scrolled out of view, you won’t see the error unless you automatically scroll the form back which is even worse, especially for people with vertigo, etc.
Also, I find it irritating when you go straight from the last field you typed in, again at the top of a form, to clicking the submit button only to find that last field was wrong.