Forms Float labels
Demo #
Examples:
Pico + Blades with Float Label CSS v2 included
Tailwind v4 + Float Label CSS v2
Float Label CSS v2 only (no CSS frameworks)
Killer features #
- No JS — pure CSS
- No hacks with
requiredand:valid
↳ HTML5 validation support - Compatible with
<select>and<textarea>fields - No need to define
for="..."attribute on<label> - NEW in v2: Finally, the text label can be placed BEFORE the input field!
- NEW in v2: Class-less!
How it works #
First, we target either:
<label>which:hasinner form inputs (classless approach)- or explicit
.has-float-labelclass (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.