Cover image for Animating forms like a semi pro ✨

Animating forms like a semi pro ✨

cydstumpel profile image Cyd Updated on ・4 min read

Have you ever come across a form which used placeholders in stead of labels? THE HORROR.

Placeholders disappear after you start writing, but also when they're prefilled by Chrome for example. This means you can't check if Google put your address actually in the address input, all in all; VERY bad user experience (UX).

This article is about the front end of forms only, I'm no expert in posts and javascript validation.

Head straight to the fancy stuff.


Animating forms like a semi pro begins with a good setup of a form.

<form class="form">
  <div class="form__input-container form__input-container--text">
    <input type="text" class="form__input form__input--text" id="firstname" name="firstname" placeholder=" " />
    <label class="form__label form__label--text" for="firstname">First name: </label>

I use BEM syntax for classes, this makes it really easy to style similar elements like inputs, but for clarity's sake I'll just use the element names in this article.

I encased the input and label in a div, this will make it much easier to style later on. I added a name attribute to the input element, without this the input is useless and will not send any data. I used the for attribute on the label to link it to the ID attribute on the input, now if you click the label you'll focus on the input. I added the label after the input to be able to style it with sibling selectors :).

Pseudo classes!

Form elements have a lot of pseudo classes available, you can check the state of an input with things like :valid, :checked etc.

input:checked + label::after {
  left: 0.3rem;    

Selecting the label's ::after pseudo element that is a sibling of the checked input.

It's possible to add multiple pseudo classes at once:

input:placeholder-shown:not(:focus) + label { 
  position: static;
  padding: 10px 20px;
  font-size: 1rem;

The :placeholder-shown pseudo class is still experimental, you should only use it for enhancements, not features. It's an amazing enhancement though, you do need to put a placeholder attribute on the input element for it to work, but it can be empty!

The :not pseudo class allows you to check if an input is not :focused, CRAZY inception type stuff, but very fun.

Validating with css

Some types of input elements (like type="email") have their own built in validation, you can also add a pattern attribute to inputs to be able to validate them with css.
It always reaaally bugged me that validation already started before you were even done typing. I admitted before that :placeholder-shown is not well supported so this is more of a future thing, but I found a way to only show errors and validation after you filled an input and weren't focussed on it anymore:

input:not(:placeholder-shown):not(:focus):invalid {
  border-bottom: red 2px solid;

input:not(:placeholder-shown):not(:focus):valid {
  border-bottom: green 2px solid;

Animating custom radio buttons

I always create custom radio buttons and because it is still not possible to animate the browser's standard ones I create them by using ::before and ::after pseudo elements.

input[type="radio"] + label {
  position: relative;
  display: block;
  padding-left: 30px;
  border-radius: 1rem 0 0 1rem; // t
  overflow: hidden;

// we're actually creating our own radio button styles
input[type="radio"] + label::before,
input[type="radio"] + label::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
input[type="radio"] + label::before {
  // this makes up the border around the input
  width: 1rem;
  height: 1rem;
  border: 1px solid black;
  border-radius: 50%;
input[type="radio"] + label::after {
  // this makes up the dot to indicate if it's selected or not
  width: 0.5rem;
  height: 0.5rem;
  background: black;
  border-radius: 50%;
  top: 50%;
  left: -10px;
  // because the label has a `overflow: hidden` style the dot will be invisible at -10px
  transform: translateY(-50%);
  transition: left 0.4s;
input[type="radio"] {
  opacity: 0;
  width: 0;
  height: 0;
  padding: 0;
input[type="radio"]:checked + label::after {
  left: 0.3rem; // this makes the dot visible again, only when the input is checked.

Editing focus states

I know the standard focus states of really every browser is to say the least probably not conformant to your website's style. But before you remove it all together think about the keyboard dependent users out there! It's really easy to add some kind of focus style (it doesn't need to be dotted or blue) and it's not only disabled people who use the keyboard it's also a lot of developers!

N.B. You can do a lot more with pseudo classes this is just the tip of the iceberg, for example you can check if an input is :disabled.
Css/HTML validation is **not
* an alternative to JavaScript validation, it's a lot easier to do complex checks with a script than write it in an input pattern attribute.*

The fancy form

Posted on Dec 31 '19 by:

cydstumpel profile



Freelance Web developer from Amsterdam


markdown guide

The BEM notation is super handy and important while creating such stuff. I'm glad that you included it.

Btw, the animations look amazing!


codepen.io refused to connect.