Forms Float labels

Available standalone  ›

Demo

Demo form

Examples:


Killer features


How it works

First, we target either:

  1. <label> which :has inner form inputs (classless approach)
  2. or explicit .has-float-label class (explicit approach)
label:has(> span):has(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select),
.has-float-label {
  display: block;
  position: relative;

Then, we define the default/fallback state (when the float label should be minimized):

> span,
label {
  position: absolute;
  left: 0;
  top: 0;
  cursor: text;
  font-size: 75%;
}

Finally, we detect if placeholder is shown, but not in focus. That means we can safely hide it, and enlarge the float label instead:

*:placeholder-shown:not(:focus)::placeholder {
  opacity: 0;
}
&:has(*:placeholder-shown:not(:focus)) {
  > span,
  label {
    font-size: inherit;
    opacity: 50%;
  }
}
}

The :has(*:placeholder-shown:not(:focus)) trick allows this input state information to propagate to the parent level. This enables modern CSS to target inner float label (<span> or <label>) regardless of its position relative to the input field.

Historically, this was not possible: the float label had to be placed after the input field to be targeted using the input:focus + label selector.