Using CSS :placeholder-shown for Floating Labels
The CSS :placeholder-shown pseudo-class provides a
pure-CSS method for implementing floating label patterns by detecting
whether an input field currently contains text. Historically, floating
labels required JavaScript to track user input and toggle CSS classes to
keep labels elevated when fields were populated. By pairing
:placeholder-shown with adjacent sibling combinators,
developers can build robust, high-performance form animations entirely
within CSS, reducing script overhead and improving user experience.
The Floating Label Mechanism
A floating label sits inside an input field like placeholder text when the field is empty. When the user focuses on the field or enters a value, the label scales down and transitions to the top edge of the input.
The primary challenge of pure-CSS floating labels is determining
whether the input has user-entered content while the field is blurred.
While the :focus pseudo-class handles the active state, it
cannot detect persistent values. The :placeholder-shown
pseudo-class solves this by targeting any form control that is currently
rendering its placeholder text.
When the field is empty, the placeholder is visible, triggering
:placeholder-shown. Once the user types even a single
character, the browser hides the placeholder, and the
:placeholder-shown state is dropped.
Structural Requirements in HTML
To manipulate the label based on the input's state, the
<input> element must precede the
<label> element in the DOM tree. This ordering allows
the use of the adjacent sibling combinator (+).
Additionally, the input must have a non-empty placeholder
attribute to activate the selector; a single whitespace character
(placeholder=" ") is often used so the native placeholder
remains invisible while still enabling the CSS state.
<div class="field-container">
<input type="text" id="username" placeholder=" " />
<label for="username">Username</label>
</div>Implementing the CSS Logic
The implementation relies on styling the label relative to the
input's :placeholder-shown state and overriding it during
:focus or when the input contains a value.
- Default (Floating) State: Style the label in its small, elevated position by default, or define it via transitions.
- Resting (Placeholder) State: When
input:placeholder-shownis active and not focused, transform the label downward and scale it up so it resembles standard placeholder text. - Active/Filled State: When the input receives
:focusor matches:not(:placeholder-shown), return the label to its floated position.
.field-container {
position: relative;
}
/* Position the label inside the container */
.field-container label {
position: absolute;
top: 0.5rem;
left: 0.75rem;
transition: transform 0.2s ease, font-size 0.2s ease, color 0.2s ease;
pointer-events: none;
font-size: 0.75rem;
color: #666;
}
/* Rest the label inside the field when empty and unfocused */
.field-container input:placeholder-shown:not(:focus) + label {
transform: translateY(0.5rem);
font-size: 1rem;
color: #999;
}
/* Float the label on focus */
.field-container input:focus + label {
transform: translateY(0);
font-size: 0.75rem;
color: #0066cc;
}Advantages of Using :placeholder-shown
Using :placeholder-shown eliminates DOM mutations, event
listeners, and layout thrashing associated with JavaScript-driven label
states. It functions smoothly with browser autofill features, works
reliably across all modern browsers, and ensures form interactions
remain responsive even on low-powered devices.