CSS-only floating label

peiche profile image Paul ・2 min read

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:

Floating label GIF

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:


(Note: as far as I know, CodePen embeds aren't yet supported on dev.to. Please correct me if I'm wrong!)

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.

This post was originally published on eichefam.net.


markdown guide

You're correct about the lack of CodePen tag, will try to get it up soon.


I believe there is a way to get around the required requirement.
Use .form-control:not(:placeholder-shown) + .form-control-placeholder.

On another note, :pseudo + .sibling doesn't work in IE or Edge.


Since writing this, I have learned a lot about the :placeholder-shown pseudo-class. Thanks!


...and placeholder-shown is experimental and does not work with IE or Edge.


It gets worse.

IE has :-ms-input-placeholder as a selector for :placeholder-shown and Edge does not.

Whereas Edge has ::-ms-input-placeholder as an element for ::placeholder and IE does not.


A small drawback is that you now can't use the form constraints API anymore to validate in the front-end.


Well done dude! This is perfect for what I was after!