Floating labels
Module — import
actual-css/css/forms/floating-fieldoractual-css/css/forms/all.
.floating-field turns a control and its label into a floating-label cell:
the label rests inside the control and floats to the top when the control is
focused or filled. It composes the existing .field layout and the text
controls (.input, .textarea, .select) with zero JavaScript.
The cell holds exactly one control and its label, with the control before the
label. Label placement is linked through the general sibling combinator, so an
injected node between the two (a password manager, an extension wrapper) is
tolerated. Help and error messages stay in the surrounding .field, otherwise
the resting label centers on the whole field instead of the control.
<div class="field">
<label class="floating-field">
<input class="input" type="email" placeholder=" " aria-describedby="email-help" />
<span class="field-label">Email</span>
</label>
<span class="field-help" id="email-help">We'll never share your email.</span>
</div>
Every .input and .textarea needs placeholder=" " so :placeholder-shown
can detect emptiness — the space keeps the placeholder invisible while making
the state detectable. :autofill is covered too, so a password manager that
prefills a field still lifts the label.
Textarea
A textarea's empty label starts at the control's normal first-line position, then floats to the same top anchor as the other controls when focused or filled. The control still reserves headroom so its text position never jumps.
<div class="field">
<label class="floating-field">
<textarea class="textarea" rows="4" placeholder=" "></textarea>
<span class="field-label">Message</span>
</label>
</div>
Select and date/time inputs
Selects have no :placeholder-shown signal, and date/time inputs render their
placeholder inconsistently (Firefox shows none). These controls stay floated in
every state, so their label always sits at the top.
<div class="stack">
<label class="floating-field">
<select class="select">
<option value="">Choose a country</option>
<option>Belgium</option>
<option>France</option>
<option>Netherlands</option>
</select>
<span class="field-label">Country</span>
</label>
<label class="floating-field">
<input class="input" type="date" />
<span class="field-label">Start date</span>
</label>
</div>
Size and density
The reserved headroom responds to control density through --control-size:
.sm and .lg on the cell resize the control geometry and typography. A
.compact or .spacious ancestor changes geometry only, including the label
spacing with it; typography stays unchanged under density.
The floating label does add height — the headroom reserves a second vertical
line on top of the control's minimum size.
CSS hooks
--floating-label-top— shared top anchor for floated labels.--floating-label-opacity— resting label opacity; floated labels return to full opacity.--floating-pad-block-start— headroom reserved above the control for the floated label; it also anchors an empty textarea label to its first text line.
Validation and focus come from the existing form contracts: the control's
border and focus ring flip through --form-invalid-border and the shared
control focus rules. Coloring the label itself on focus or invalid state is
left to the surrounding recipe — the joined floating form in the
blocks demo shows one such recipe.
Not currently compatible with
.input-icon; use standard labels when an embedded leading icon is required.