Freewsad
HomeBlogsBooks Categories
Log inRegister
Menu
Freewsad - The best educational website

HomePrivacy PolicyAbout UsContact UsCategories

Copyright© 2021 - 2025 All rights reserved.

Progressively Enhanced Form Validation, Part 1: HTML and CSS

Progressively Enhanced Form Validation, Part 1: HTML and CSS

At 4/19/2024

Four icons. Icon 1: representing an invalid state, a fuscia circle shape with a white exclamation mark in the center. Icon 2: representing a valid state, a green circle shape with a white checkmark in the center. Icon 3: representing HTML, an orange badge shape with a white HTML element open/closing tag in the center. Icon 4: representing CSS, a blue badge shape with a light-blue paint brush in the center.

Browsers nowadays have built-in form validation (or “constraint validation”) features that are super helpful for highlighting when the entered data satisfies the necessary criteria. Some of us have relied on JavaScript-only solutions to handle client-side form validation in the past (/me slowly raises hand…), but as it turns out, most of these built-in features have been around for over a decade!

HTML5 introduced new mechanisms for forms: it added new semantic types for the <input> element and constraint validation to ease the work of checking the form content on the client side. Basic, usual constraints can be checked, without the need for JavaScript, by setting new attributes; more complex constraints can be tested using the Constraint Validation API.

MDN Constraint Validation docs

Join along as we explore progressively enhancing the form validation experience through a series of articles. Starting with the browser’s built-in validation features using HTML and CSS, then layering JavaScript for more enhancements, each successive article will build on the previous.

Feel free to view this article’s accompanying demo as a reference. The source code is available on GitHub.

I’m excited and hope you are too. Let’s dive into the foundation of any website: HTML and CSS.

Permalink to Browser built-in form validation as the foundation

Browser built-in form validation as the foundation

Native form validation features can validate most user data without relying on JavaScript. These features provide the strong foundation required for building a progressively enhanced experience.

Let’s take a closer look at each feature.

Permalink to Input type attributes

Input type attributes

It starts with choosing the most semantically appropriate value for the input type attribute (e.g., type="email").

<label for="customer-email">Email</label>
<input
  id="customer-email"
  name="customerEmail"
  type="email"
/>
Code language: HTML, XML (xml)
Permalink to Extra validation attributes

Extra validation attributes

Other validation attributes can be added to layer more constraints (e.g., required, min/max, minlength/maxlength, pattern, etc.).

<label for="customer-email">
  Email <span aria-hidden="true">(required)</span>
</label>
<input
  id="customer-email"
  name="customerEmail"
  type="email"
  required
  minlength="2"
/>
Code language: HTML, XML (xml)
Permalink to CSS pseudo-classes

CSS pseudo-classes

Value-checking pseudo-classes are available to help style form controls. Depending on your site’s design, you may not need all of them, but it’s good to know they exist.

Permalink to Validity pseudo-classes

Validity pseudo-classes

To highlight valid or invalid fields.

  • :valid
  • :invalid
Permalink to Optionality pseudo-classes

Optionality pseudo-classes

To highlight required fields (specified by a required attribute) and optional fields (fields with no required attribute).

  • :required
  • :optional
Permalink to Range pseudo-classes

Range pseudo-classes

To highlight fields within or outside of the range limits specified by the field’s min/max attributes.

  • :in-range
  • :out-of-range
Permalink to User-interaction pseudo-classes

User-interaction pseudo-classes

To highlight valid or invalid fields after the user has interacted with the field.

  • :user-valid
  • :user-invalid
Permalink to Error message bubble

Error message bubble

When a form is submitted, the browser automatically shows error messages in a “bubble” next to the first form control where the input data does not satisfy the form control’s validation constraints. The message and design of the bubble will differ from browser to browser. The styles of the error bubble cannot be modified, providing a consistent browser-specific user experience. However, this can also be a negative as the bubble design may not fit the site’s aesthetics.

The Chrome browser built-in error message bubble floating over an empty email field with the message, "Please fill out this field."
Each browser has its own error message bubble design. Shown above is Chrome’s design.

Cloud Four’s latest insights and articles, straight to your inbox

Permalink to How to avoid invalid styles showing on page load

How to avoid invalid styles showing on page load

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. I agree. This creates a confusing user experience.

A series of required empty input fields showing the "invalid" UI state on page load.
Seeing invalid form inputs on page load is a confusing user experience.

No worries, though, :user-invalid/:user-valid to the rescue!

These CSS pseudo-classes can progressively enhance the user experience in browsers that support them. In contrast to :invalid/:valid, using the :user-invalid/:user-valid pseudo-classes will apply the styles only after the user has interacted with the form control.

No more invalid UI styles on page load!

To support all modern browsers and apply :user-invalid/:user-valid in a progressively enhanced manner, the CSS could look something like this:

/**
 * For browsers that support :user-invalid/:user-valid
 */
input:user-invalid {
  /* Invalid input UI styles */
}
input:user-valid {
  /* Valid input UI styles */
}

/**
 * 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:invalid {
    /* Invalid input UI styles */
  }
  input:valid {
    /* Valid input UI styles */
  }
}Code language: CSS (css)

We can see the differences when viewing the Part 1 demo:

  • In browsers that support :user-invalid (e.g., new versions of Firefox and Safari), we’ll notice invalid styles do not get applied until after we interact with and move away from (blur) the form control.
  • In browsers that do not support :user-invalid (e.g., Chrome, though they are looking to ship support soon!), we’ll notice the invalid styles are applied when the page loads before we interact with the form controls.
Permalink to What if I need IE support?

What if I need IE support?

The @supports CSS at-rule is supported in all modern browsers. But if the project needs to support IE, we’ll want to style the :invalid/:valid CSS rules outside the @supports block and then unset those styles inside the @supports block.

Permalink to Browser built-in validation doesn’t do everything

Browser built-in validation doesn’t do everything

As with all things, there are always pros and cons. Let’s take a quick step back to review a few pros and cons of using browser built-in validation as the foundational base user experience.

Permalink to Pros

Pros

  • No JavaScript is 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
Permalink to Cons

Cons

  • The error messages are not customizable (without JS)
  • The error message bubble design may not fit the overall site design
  • The :invalid styles are applied before the user has a chance to fill out the form field in browsers that do not support :user-invalid
  • Some form controls (e.g., a checkbox group) cannot be validated
  • Safari has a few open :user-invalid related bugs (three open tickets as of this article’s publish date)
  • Web Content Accessibility Guidelines (WCAG) 2.1 Success Criterion (SC) violations:
    • SC 1.3.1: Info and Relationships (Level A) – The validation error message is not associated with the field in error
    • SC 1.4.4: Resize Text (Level AA) – The error message bubble text does not resize
    • SC 1.4.12: Text Spacing (Level AA) – The error message bubble text does not respect text spacing styles
    • SC 2.2.1: Timing Adjustable (Level A) – There is no control over the timing of the error message bubble, and in most cases, it disappears too quickly (Chromium is considering closing an open issue that should absolutely be addressed, “star” the issue and show support!)
    • SC 3.3.1: Error Identification (Level A) – The field in error is not identified
    • SC 3.3.2: Labels or Instructions (Level A) – The reason for the error is not always clear
    • SC 3.3.3: Error Suggestion (Level AA) – The error message may not always be clear or provide a helpful suggestion

Thank you to Juliette Alexandria and Adrian Roselli for providing feedback and resources regarding WCAG violations for native browser validation features. Your feedback is greatly appreciated. 🙌🏽

Permalink to Can the experience be enhanced and made more accessible?

Can the experience be enhanced and made more accessible?

You bet it can! We can address all of these cons by layering a bit of JavaScript combined with some ARIA attributes. We can build a progressively enhanced, more accessible experience on top of the native HTML & CSS validation features.

This is a fantastic example of how progressive enhancement shines. We provide a baseline user experience without JavaScript. The enhanced version builds on an already established foundation. We no longer need to rely on JavaScript-only solutions.

In the following article of this series, we’ll explore all of this, building out from where the Part 1 demo left off. See you there!

Permalink to More resources

More resources

  • Avoid Default Field Validation by Adrian Roselli
  • MDN Web Docs: Input pseudo-classes
Permalink to Missed an article in the series?

Missed an article in the series?

I’ve got you! Listed below are all of the articles from the series:

  • Part 1: HTML and CSS
  • Part 2: Layering in JavaScript
  • Part 3: Validating a checkbox group
  • Part 4: Custom validation messages
Copyrights

We respect the property rights of others and are always careful not to infringe on their rights, so authors and publishing houses have the right to demand that an article or book download link be removed from the site. If you find an article or book of yours and do not agree to the posting of a download link, or you have a suggestion or complaint, write to us through the Contact Us, or by email at: support@freewsad.com.

More About us