CSS Float Label
Bulletproof classless CSS-only implementation of Float Label pattern with automatic fallback for ANY non-supporting browser.
/anydigital/float-label-css
from /anydigital/blades
Table of Contents
Demo
Drop-in support for Pico's /docs/forms :
More examples:
- Float Label CSS v2 only (without any framework)
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!
Install
Via CDN:
Just Float Label CSS:
<link href="https://cdn.jsdelivr.net/npm/@anydigital/blades@^0.27.0-beta/assets/float-label.min.css" rel="stylesheet" />
Or with all CSS Blades included:
<link href="https://cdn.jsdelivr.net/npm/@anydigital/blades@^0.27.0-beta/assets/blades.min.css" rel="stylesheet" />
Via npm:
npm install @anydigital/blades
Then import just Float Label CSS:
@import "@anydigital/float-label";
Or with all CSS Blades included:
@import "@anydigital/blades";
Usage
Simply place <input> inside <label> to enable float label:
<label>
<span>Email</span>
<input type="email" placeholder="email@example.com" />
</label>
NOTE: W3C allows this!
Alternatively wrap <label> and <input> inside .has-float-label:
<div class="has-float-label">
<label for="email">Email</label>
<input id="email" type="email" placeholder="email@example.com" />
</div>
NOTE:
for="..."attribute is required on<label>in this case
Credits
- Matt D. Smith for original pattern
- Emil Björklund for
:placeholder-shown - Anton Staroverov for
:has(*:placeholder-shown:not(:focus)) label- to finally support labels before input fields
- instead of
input:focus + labelused previously
Browser support
Any browser with /css-placeholder-shown,
/css-has and
/css-nesting.
All non-supporting browsers should gracefully fall-back to the default layout.
=> Can be used in ANY browser as is!
Featured by:
/pryley/float-labels.js
/uptonking/note4yaoo/blob/main/toc/toc-lib-css-only.md#css-tools
/bcgov/moh-fpcare
package.json
How it works
First, we target either:
<label>which:hasinner form inputs (classless approach)- or explicit
.has-float-labelclass (alternative approach)
label: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.