CSS only floating labels

I recently learned about the pseudo CSS class :placeholder-shown. Obviously, it is a class that is added only when input placeholder is shown. But what that also means is that the input is empty:

/* Input is empty */
input:placeholder-shown

By negating it with :not() we can detect if the input has value:

/* Input has value */
input:not(:placeholder-shown)

I created a demo that uses these two CSS snippets to highlight input's current state. There is no JavaScript involved.

Input has value
Input is empty
Focused

Real world use cases

I recently used this to conditionally display a clear button next to an input. But it can also be used to create floating labels.

However, it's important to note that anything you want to style based on the input's state needs to be positioned after the input element.

Floating labels

For a floating label, the HTML structure should look something like this:

<input />
<label>Floating label</label>

The floating label needs to be repositioned in two scenarios: when the input is focused and when it has a value.

/* Input has value */
input:focus + label,
input:not(:placeholder-shown) + label {
  /* Move the label above the button and scale it down */
  transform: translateY(-100%) scale(0.75);
}

Clear button

Similarly, we'll put the clear button after the input:

<input />
<button>Clear</button>

and hide it when input is empty:

/* Input is empty */
input:placeholder-shown + button {
  /* Hide the clear button */
  display: none;
}

Demo

Focus an input to make its label float, and type something to make the clear button appear.

You can also play with the code on CodePen.

Safari gotcha

Be aware that in Safari the :placeholder-shown pseudo class will only be added if the input has the placeholder attribute explicitly defined.

Which means that we have to define it, but if we don't need the placeholder, we can easily hide it using CSS:

input::placeholder {
  color: transparent;
}