Material Forms and Validation Without JavaScript (HTML/CSS Only)

Brenton Klik
6 min readApr 11, 2022

--

As a web application developer, it is easy to fall into thinking that JavaScript is the answer to every question. We grow comfortable using our framework of choice and it becomes the core approach to every new product. There is, however, a different approach to development. You start with regular old HTML/CSS and build out a completely functional tool. Then, you layer on features with JavaScript. This is called, Progressive Enhancement.

Progressive Enhancement ensures that your website is functional across the highest number of browsers and configurations. HTML and CSS have come a long way since the 90s. Even without JavaScript you can provide a lot of visual appeal and functionality.

The Form

I recently wrote an article about Validating forms with the Quasar Framework and Vue.js. I wanted to revisit that form and show how much of this form could be recreated using only HMTL and CSS.

Note: I am a person who learns by doing, if all you care about is the end result, you can skip to the bottom of the article to see the completed markup and styling.

HTML

If you are unfamiliar with section, header, and footer tags, they are newer semantic elements. You can have many header and footer tags on a page as long as they belong to their own section tag.

You will notice that the label tags come after the input tags. This is so we can use the + selector in CSS to change its style based on different states of the input.

SCSS

You will notice that we are using rem to size everything. By making the base font size 8px, you can then size everything by factors of 8 (1: 0.125rem, 2: 0.25rem…16: 2rem, etc.). This makes everything related to the size of the font and keeps everything proportional when font sizes are changed by the user for readability.

We are also using a background image to draw a border. This way the border is not computed as part of the size of the input and will become important when we get to the animation step.

Note: I am using SCSS. You will need to preprocess this, using something like node-sass, into a CSS file called material.css before using it.

We now have a form that looks like this…

Not all the form can be created without JavaScript. But we should be able to get the following:

  • Labels that animate themselves above a field when focused
  • Input bottom border that animates in when focused
  • Validation that prevents form submission for require fields, email, phone, and password criteria.
  • Basic ripple effect when buttons focused.

Animating Labels

Currently the labels cover up the text we type in the input. What we want instead is to have the labels move above the cursor when it has focus.

To do this, we need to add a transition to the label so it animates when its properties change.

label {

transition: all ease 300ms
}

Then we will want to change the label’s properties to move it up with a new color when the input has focus. Inside our form.material .field input style will we add.

&:not(:placeholder-shown) + label,
&:focus + label {
color: $primary;
font-size: 1.5rem;
top: 0;
}

On each of the input tags we need to add an empty placeholder attribute to trigger the :not(:placeholder-shown) selector.

<input type="name" name="name" id="name" placeholder=" "/>

We should now have something that looks like this.

Animating Input Border

When the user has focused an input field, we want the line at the bottom of the field to animate in with the primary color. We also want the line to remain a little darker if the field has input after to loses focus.

To do this, we are going to add multiple background images, and animate them independently.

input {
background-image:
linear-gradient(
to bottom,
transparent calc(100% - 0.25rem),
$primary 0.25rem
),
linear-gradient(
to bottom,
transparent calc(100% - 0.125rem),
#ccc 0.125rem
);
background-position: center bottom;
background-repeat: no-repeat;
background-size: 0, 100%;
...
transition: all ease 300ms;
&:focus {
background-size: 100%, 100%;
}
&:not(:placeholder-shown) {
background-image:
linear-gradient(
to bottom,
transparent calc(100% - 0.25rem),
$primary 0.25rem
),
linear-gradient(
to bottom,
transparent calc(100% - 0.125rem),
#999 0.125rem
);
}
...
}

This bit of styling adds a second blue line background that is layered on top of the light grey line. This image is hidden by the fact its size is set to 0%. When the input receives focus, the background size is changed to 100%, and the transition handles the animation.

We now have an input that looks like this.

Form Validation

One of the easiest things to validate are the required fields. All you need is a required attribute on any required input.

<input type="name" ... required/>

Let’s take this one step further by adding a new $negative color and adding a red * after each label for a required input.

$negative: tomato;...input {
...
&:required + label::after {
color: $negative;
content: ' *';
}
...
}

Email validation is automatically taken care of by its type.

To validate the phone number and password we will use the pattern attribute and regex.

<input 
type="tel"
pattern="[(][0-9]{3}[)] [0-9]{3} [-] [0-9]{4}"
.../>
<input
type="password"
pattern="^(?=.{12,})(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*\-_+=]).*$"
.../>

Button Ripple

To get a basic ripple effect, we are going to use a method similar to the underline we used for the input. Instead of using a linear-gradient, we’re going to use a radial-gradient instead.

button.material {
...
background-image:
radial-gradient(circle at center, #0003 50%, transparent 50%);
background-repeat: no-repeat;
background-position: center center;
background-size: 0;
...
&:focus {
background-size: 225%;
}
&.flat {
background-color: transparent;
box-shadow: none;
&:focus,
&:hover {
background-color: #eee;
}
}
&.primary {
...
background-image:
radial-gradient(circle at center, #fff3 50%, transparent 50%);
...
&:focus,
&:hover {
box-shadow: 0 0.125rem 0.325rem 0.125rem #0009;
}
}
}

We should now have buttons that behave like this.

The Result

We should now have a form that looks and functions something like this.

Our HTML should look something like this.

And, our CSS should look something like this.

Conclusion

Even without JavaScript, we have been able to make a form that follows many of the Material guidelines, ensures that all required fields have been filled in, and the correct data has been entered before submission.

Standard HTML and CSS prove to be powerful on their own and should not be overlooked even when using Web Applications frameworks.

--

--

Brenton Klik
Brenton Klik

Written by Brenton Klik

Brenton Klik is an Interaction Designer in the Washington DC area. He designs interfaces and experiences for web, mobile, and desktop platforms.

Responses (2)