CSS-only floating label

One of those nifty things Google did with the Material Design guidelines is include floating labels.

Don’t know what a floating label is? Check it out:

Being the ever-curious sort that I am, I wondered if this could be accomplished sans JavaScript. Turn out, it can, but only for required fields.

Here’s the demo:

To make this work, the markup has to be structured in a very specific way: the input has to be in front of the label. (Don’t forget the for attribute on the label!)

In this example, I’ve given the label a class, form-control-placeholder, and made it absolutely positioned. The container, which holds the label and input, is set to be relatively positioned, so the label can act as an overlay to the input itself, appearing as the placeholder.

.form-group {
  position: relative;
  margin-bottom: 1.5rem;

.form-control-placeholder {
  position: absolute;
  top: 0;
  padding: 7px 0 0 13px;
  transition: all 200ms;
  opacity: 0.5;

Then we add a style to move the label above the input when it (the input) has focus. This is why we put the input before the label; CSS only works in one direction (although changing this is on many a developer’s wish list for CSS 4.0, including yours truly).

.form-control:focus + .form-control-placeholder {
  font-size: 75%;
  transform: translate3d(0, -100%, 0);
  opacity: 1;

Unfortunately, this is not enough. As soon as the input loses focus, the label will shoop right down again, covering up the input, not caring at all about whether or not you just entered some text there. (Yes, in my head the label makes a shoop sound whenever it moves.)

There is no way to determine via CSS alone whether an input has something entered. The closest we can get is the :valid selector, which is only fulfilled on inputs that have the required parameter.

So, we update the CSS to include :valid:

.form-control:focus + .form-control-placeholder,
.form-control:valid + .form-control-placeholder {
  font-size: 75%;
  transform: translate3d(0, -100%, 0);
  opacity: 1;

And now we have a working example of CSS-only floating labels. Unfortunately, there’s no way to get around that required, um, requirement, without resorting to JavaScript.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.