unscripted/ui

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-destructive styles 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" + required here, or pattern, minlength, min/max for anything else.
  • placeholder:text-muted-foreground and disabled:opacity-50 cover 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 from type="number".
  • pattern="[0-9]{6}" does the validating (six digits), and user-invalid:border-destructive paints the field red after they tab away, the same honest, post-interaction validation as above. Before inputmode shipped, 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.

FeatureChromeEdgeFirefoxSafariWhen missing
:user-invalid validation stylingBaseline · widely available1191198816.5No live invalid styling; the field still validates normally on submit.