Input
A styled text input with free validation states — :user-invalid flags bad values with zero JavaScript, and only after the user actually touched the field.
:user-invalid
Native input validation
user-invalid:border-destructivestyles the field only after interaction. Unlike:invalid, it won’t scream at an empty required field on page load.- Validation comes from the platform:
type="email"+requiredhere, orpattern,minlength,min/maxfor anything else. placeholder:text-muted-foregroundanddisabled:opacity-50cover the remaining states.
Number input
Card numbers, verification codes, phone and account numbers look numeric, so the reflex is
type="number". Resist it. That type treats digits as a quantity: it bolts on spinner buttons
nobody asked for, lets a stray scroll-wheel tick silently change the value, and quietly drops any
character it can’t parse as a float, with no warning to the user and nothing announced to their
screen reader. A verification code isn’t a magnitude; getting the last digit wrong by one is exactly as
wrong as getting every digit wrong. (The GOV.UK team walked this exact path.)
type="text"hands control back to the user and stops the browser “helpfully” mangling the value.inputmode="numeric"still summons the big numeric keypad on phones, the one thing most people actually wanted fromtype="number".pattern="[0-9]{6}"does the validating (six digits), anduser-invalid:border-destructivepaints the field red after they tab away, the same honest, post-interaction validation as above. Beforeinputmodeshipped,pattern="[0-9]*"was the trick that coaxed the numeric keypad out of old iOS.- Reserve
type="number"for values you’d genuinely add, subtract, or step through: quantities, ratings, years.
Browser support
Minimum stable version per engine, resolved at build time from MDN's browser-compat-data; Baseline status from the official web-features dataset. Everything degrades gracefully — the “when missing” column is the actual behavior, not a broken page.
| Feature | Chrome | Edge | Firefox | Safari | When missing |
|---|---|---|---|---|---|
| :user-invalid validation stylingBaseline · widely available | 119 | 119 | 88 | 16.5 | No live invalid styling; the field still validates normally on submit. |