Introduction
We do a lot of form validation in web development, starting from the frontend, to the backend where the data goes. Traditionally, In the frontend side, we rely heavily on JavaScript for this task. However, modern CSS, combined with HTML5 features, are more equipped than ever for client-side form validation.
In this article, we’ll look into how to validate forms using CSS.
HTML5 Form Attributes
In order for to validate our forms, these HTML attributes defines our requirements, and are relied upon for the validation in CSS.
required
: Specifies that an input field must be filled out.pattern
: Defines a regular expression that the input’s value must match.minlength
andmaxlength
: Set the minimum and maximum length for text input.min
andmax
: Set the minimum and maximum values for numerical input.type
: Specifies the type of input (e.g., email, number, url).
Example:
<form>
<input type="text" required minlength="3" maxlength="20" />
<input type="email" required pattern="[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,}$" />
<input type="number" min="0" max="100" />
<input type="submit" value="Submit" />
</form>
CSS Pseudo-classes for Form Validation
On the CSS side of this, it provides several pseudo-classes that allow us to style form elements based on their validation state:
:valid
: Applies to elements with valid input.:invalid
: Applies to elements with invalid input.:required
: Matches required elements.:optional
: Matches optional elements.:in-range
: Applies to elements with a value within specified range.:out-of-range
: Applies to elements with a value outside the specified range.
Basic Form Validation Styling
input:valid {
border: 2px solid green;
}
input:invalid {
border: 2px solid red;
}
input:required {
border-left: 4px solid blue;
}
This immediately invalidates before users even interact with these inputs.
This is not the desired behavior, as the validation should only happen when you interact with subject. Thankfully, modern CSS is much more powerful, and JavaScript wouldn’t need to intervene.
Preventing Premature Invalid Styling
One way we can solve this is introducing the :not()
and :placeholder-shown
pseudo-classes:
input:invalid:not(:focus):not(:placeholder-shown) {
border: 2px solid red;
}
input:valid:not(:placeholder-shown) {
border: 2px solid green;
}
Now, the invalid style will only apply if the input is invalid, not focused, and the placeholder is not shown (meaning the user has entered something).
Invalidation will only happen when the blurs the subject.
Notice that username remains in it default state since it has not be interacted with yet!
Know, this is definitely a trick some of us might prefer or find tasking, but there is a native way to solve this built right into CSS.
Another method is:
Instead of valid
or invalid
use the user-valid
or user-invalid
input:user-valid {
border: 2px solid green;
}
input:user-invalid {
border: 2px solid red;
}
This will allow the validation to happen only when the user interacts with it.
Custom Validation Messages
While browsers provide default validation messages, we can create custom ones using the ::after
pseudo-element and the :invalid
pseudo-class:
input:user-invalid + span::after {
content: '⚠ Please enter a valid value';
color: red;
display: block;
margin-top: 5px;
}
input[type='number']:user-invalid + span::after {
content: '⚠ Please enter a number between 1 - 19';
color: red;
display: block;
margin-top: 5px;
}
Remember to add a
<span>
element after each input in your HTML for this to work.
Styling Specific Input Types
Different input types may require different validation styles. Here’s an example for email inputs:
input[type='email']:user-invalid {
background: url('error-icon.svg') no-repeat 95% 50%;
background-size: 25px;
padding-right: 30px;
}
This adds an error icon to invalid email inputs.
And hey, if you're itching to try them out
Fire up Devcanvas and start experimenting with Form controls. It's the best way to get a feel for how they work!
Range Validation Styling
For numerical inputs with min and max values, the in-range
and out-of-range
pseudo-class are available:
input[type='number']:in-range {
background-color: #e8f0fe;
}
input[type='number']:out-of-range {
background-color: #ffdddd;
}
Password Strength Indicator
Using these capabilities, we can create a simple password strength indicator using just HTML5 and CSS:
<form>
<input type="password" id="password" pattern="(?=.*d)(?=.*[a-z])(?=.*[A-Z]).{8,}" required />
<div class="strength-meter"></div>
</form>
Don’t try to understand the
regex
, just take it as it is 😅 .
#password:user-valid + .strength-meter::before {
content: 'Strong password';
color: green;
}
#password:user-invalid + .strength-meter::before {
content: 'Weak password';
color: red;
}
#password[pattern]:user-valid + .strength-meter::before {
content: 'Very strong password';
color: darkgreen;
}
This provides feedback based on whether the password meets the specified pattern.
Styling Form Submission
Also, while the form is invalid, we can disable the submit button just by targeting the :invalid pseudo-class:
form:invalid button[type='submit'] {
opacity: 0.5;
pointer-events: none;
}
Accessibility Considerations
While visual cues are helpful, we should also consider users who rely on screen readers. We can use aria-invalid
and aria-describedby
attributes to improve accessibility:
<input type="email" required aria-describedby="email-error" />
<span id="email-error" role="alert"></span>
input:invalid:not(:focus):not(:placeholder-shown) {
aria-invalid: true;
}
input:user-invalid + #email-error::after {
content: 'Please enter a valid email address';
}
However, it’s important to note that CSS validation is a visual only form of validation that can significantly reduce the need for JavaScript, it’s not a complete replacement for server-side validation. Always validate form submissions on the server to ensure data integrity.
As browser support for new CSS features continues to improve, we can expect even more awesome styling and improved capabilities in the future.
Resources:
The :user-valid and :user-invalid pseudo-classes
CSS3 Pseudo-Classes and HTML5 Form - HTML5 Doctor
Be the first to comment!