- Built-in browser validation
- Constraint Validation API (JavaScript)
What if you could use built-in browser validation and have a custom design in a progressively enhanced manner? You can! The Constraint Validation API provides a way to customize the design of the validation error UI/UX while leveraging browser built-in features for the validation of the form data. This article explores one example of how to progressively enhance the form validation experience with the Constraint Validation API.
Browser built-in form validation features are able to validate most user data without relying on JavaScript. This provides the strong foundation required for building a progressively enhanced experience.
This can be accomplished by:
- Choosing the most semantically appropriate value for the
input
type
attribute (e.g.,type="email"
) - Adding other validation attributes to layer more constraints (e.g.,
required
) - Using validation-related pseudo-classes to style the form controls:
:valid
,:required
,:optional
,:invalid
as well as:user-valid
/:user-invalid
where supported (provides a better user experience)
- TODO Rename heading
- TODO Video? GIF?
- TODO Side-by-side comparison of
:invalid
vs:user-invalid
Image of browsers that don't support :user-invalid
One of the pushbacks against using browser built-in form validation is that :invalid
styles are applied on page load before the user interacts with the form controls. No worries! This is where a progressive enhancement layer can be applied in browsers that support :user-invalid
/:user-valid
. Using :user-invalid
/:user-valid
instead of :invalid
/:valid
will only apply the styles after the user has interacted with the form control.
To support all browsers and apply :user-invalid
/:user-valid
in a progressively enhanced manner, the CSS could look something like this:
/* Use `:user-invalid`/`:user-valid` only if supported */
@supports selector(:user-invalid) {
input:user-valid {
/* */
}
input:user-invalid {
/* */
}
}
/**
* When not supported, fallback to `:invalid`/`:valid`
* Wrapping `:valid`/`:invalid` in a "not" `@supports` block ensures that the
* invalid styles are not applied on page load in browsers that do
* support `:user-invalid`/`:user-valid`
*/
@supports not selector(:user-invalid) {
input:valid {
/* styles */
}
input:invalid {
/* */
}
}
- No JavaScript required
- No need to write custom validation logic
- Can handle the majority of validation use cases
- The validation errors are automatically rendered and styled by the browser providing a consistent browser-specific experience
- Validation error messages are localized automatically
- The validation error messages are not customizable
- The
:invalid
styles are applied before the user has a chance to fill out the form field on browsers that do not support:user-invalid
- Some form controls (e.g., a
checkbox
group) cannot be validated
TODO Work on this
This is a fantastic example of how progressive enhancement can shine. All of these cons can be addressed with JavaScript. Progressive enhancement FTW!