Selector Beginner

Form validation styles without JavaScript

:invalid fires as soon as the page loads, marking empty required fields as errors before anyone types. The workaround was JS adding a .touched class after blur. :user-invalid only activates after the user has interacted with the field.

4 lines
1input:user-invalid { border-color: red; }
2input:user-valid { border-color: green; }
3/* no JS, no .touched class */
Old 10 lines
1/* only style after .touched class is added by JS */
2input.touched:invalid { border-color: red; }
3input.touched:valid { border-color: green; }
4// JS: add .touched class on blur
5input.addEventListener('blur', () => {
6  input.classList.add('touched');
7});
Newly available Since 2023 85% global usage

Since 2023 this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

Works in all modern browsers. May need a fallback for older browsers.

119+
88+
16.5+
119+
Interop 2026 focus area ? Learn more β†’
type then leave each field to see validation
:user-invalid shows error only after interaction
Kinsta

Your first month is free

Managed WordPress hosting for faster sites.

Learn more
⚑

No blur listener

No JavaScript event listener, no .touched class, no class toggling on every field.

✦

Interaction-aware

:user-invalid only triggers after the user has interacted with the field. Empty required fields stay neutral on page load.

∞

Pairs with :user-valid

:user-valid shows success state the same way. Both follow the same interaction threshold as :user-invalid.

Lines Saved
10 β†’ 4
No JS touched class
Old Approach
.touched + JS blur
Class toggle on every field
Modern Approach
:user-invalid
Browser tracks interaction state

How it works

:invalid applies the moment the page loads. A required empty field is immediately styled as an error before the user touches it. The fix was JavaScript: listen for blur on each input, add a .touched class, then use .touched:invalid in CSS to defer the error styling until after first interaction.

:user-invalid and :user-valid are built-in pseudo-classes that match after the user has interacted with a field. :user-invalid activates when the field is left in an invalid state. :user-valid activates on a valid state. The browser tracks the interaction threshold β€” no JavaScript, no class management.

New CSS drops.

Join 600+ readers who've survived clearfix hacks.

ESC