Skip to main contentSkip to docs navigation

Floating Labels

Animate a label from placeholder text to a floating position above the input on focus or when the field holds a value.

@layer components
Depends onForm Input
Component tokens

Overview

Floating labels wrap a <label> and a form control inside .form-floating. The label sits in the placeholder position until the control gains focus or holds a value, then transitions to a floating position above it.

The label uses the :has() relational selector to react to its sibling .form-input's :focus and :placeholder-shown state, applying the floated position without extra classes or JavaScript.

Basic usage

To implement floating labels, wrap a <label> and a form control (with the .form-input class) inside a .form-floating container. The structure requires:

  1. The <label> must come before the form control in the markup — the label detects its sibling's state through the subsequent-sibling combinator inside :has()
  2. Each <input> and <textarea> requires a placeholder attribute — without one, :placeholder-shown never matches and the label stays floated even when the field is empty
  3. Each label must be properly associated with its form control using the for attribute

.form-floating does not respond to the .small and .large size modifiers. Floating inputs always render at the fixed height defined by $form-floating-height.

Example

HTML
<div class="form-floating mb-medium">
  <label for="floatingEmail">Email address</label>
  <input type="email" class="form-input" id="floatingEmail" placeholder="name@example.com">
</div>
<div class="form-floating">
  <label for="floatingPassword">Password</label>
  <input type="password" autocomplete="current-password" class="form-input" id="floatingPassword" placeholder="Password">
</div>

Pre-filled values

When there's a value already defined, <label>s will automatically adjust to their floated position.

HTML
<div class="form-floating">
  <label for="floatingInputValue">Input with value</label>
  <input type="email" class="form-input" id="floatingInputValue" placeholder="name@example.com" value="test@example.com">
</div>

Form validation

Validation classes like .is-invalid style floating label inputs the same way they style regular inputs.

HTML
<div class="form-floating">
  <label for="floatingInputInvalid">Invalid input</label>
  <input type="email" class="form-input is-invalid" id="floatingInputInvalid" placeholder="name@example.com" value="test@example.com">
</div>

Input types

Floating labels apply the same markup pattern to textareas, selects, and file inputs, with small adjustments for each control type.

Textarea

Without a rows attribute, a <textarea> defaults to two native rows and renders taller than an <input>.

HTML
<div class="form-floating">
  <label for="floatingTextarea">Comments</label>
  <textarea class="form-input" placeholder="Share your thoughts here" id="floatingTextarea"></textarea>
</div>

Set either the rows attribute or an explicit height (inline style or custom CSS) for a custom <textarea> height; rows="1" matches an <input>'s height.

HTML
<div class="form-floating">
  <label for="floatingTextareaRows1">Comments</label>
  <textarea class="form-input" rows="1" placeholder="Share your thoughts here" id="floatingTextareaRows1"></textarea>
</div>
<div class="form-floating">
  <label for="floatingTextareaHeight">Comments</label>
  <textarea class="form-input" style="height: 100px" placeholder="Share your thoughts here" id="floatingTextareaHeight"></textarea>
</div>

Select

Select menus also work inside .form-floating using the .form-input class. Unlike inputs, select menus always show the label in its floated state, since :placeholder-shown never matches a <select>.

HTML
<div class="form-floating">
  <label for="floatingSelect">Preferences</label>
  <select class="form-input" id="floatingSelect">
    <option selected>Choose an option</option>
    <option value="1">Option One</option>
    <option value="2">Option Two</option>
    <option value="3">Option Three</option>
  </select>
</div>

Select elements using the size or multiple attribute render as multi-row list boxes, which do not fit .form-floating's fixed single-line height and padding.

File inputs

File inputs can also be used with floating labels, though the visual appearance is different from other input types due to the browser's implementation of file inputs. Since <input type="file"> doesn't support the placeholder attribute, :placeholder-shown never matches it either — like selects, the label always renders in its floated state.

File inputs with floating labels may appear differently across browsers. For more consistent styling, consider custom file input implementations.

HTML
<div class="form-floating">
  <label for="floatingFileInput">Upload document</label>
  <input type="file" class="form-input" id="floatingFileInput">
</div>

Disabled inputs

Add the disabled boolean attribute to inputs, textareas, or selects to apply the disabled state styling—grayed out appearance, no pointer events, and focus prevention.

HTML
<div class="form-floating mb-medium">
  <label for="floatingInputDisabled">Email address</label>
  <input type="email" class="form-input" id="floatingInputDisabled" placeholder="name@example.com" disabled>
</div>
<div class="form-floating mb-medium">
  <label for="floatingTextareaDisabled">Comments</label>
  <textarea class="form-input" placeholder="Share your thoughts here" id="floatingTextareaDisabled" disabled></textarea>
</div>
<div class="form-floating mb-medium">
  <label for="floatingTextarea2Disabled">Comments</label>
  <textarea class="form-input" placeholder="Share your thoughts here" id="floatingTextarea2Disabled" style="height: 100px" disabled>This textarea is disabled with existing content</textarea>
</div>
<div class="form-floating">
  <label for="floatingSelectDisabled">Preferences</label>
  <select class="form-input" id="floatingSelectDisabled" disabled>
    <option selected>Choose an option</option>
    <option value="1">Option One</option>
    <option value="2">Option Two</option>
    <option value="3">Option Three</option>
  </select>
</div>

Plaintext inputs

Combine the readonly attribute with the .plaintext class on .form-input to display a value as static text without changing the surrounding layout. Because .plaintext is included directly in the label's :has() selector, the label always renders in its floated position, even when the control is empty.

HTML
<div class="form-floating mb-medium">
  <label for="floatingEmptyPlaintextInput">Empty input</label>
  <input type="email" readonly class="form-input plaintext" id="floatingEmptyPlaintextInput" placeholder="name@example.com">
</div>
<div class="form-floating mb-medium">
  <label for="floatingPlaintextInput">Input with value</label>
  <input type="email" readonly class="form-input plaintext" id="floatingPlaintextInput" placeholder="name@example.com" value="user@chassis.css">
</div>

Layout options

Floating labels compose with .input-group and the grid system for more complex form layouts.

Input groups

@
HTML
<div class="input-group mb-medium">
  <span class="input-addon">@</span>
  <div class="form-floating">
    <label for="floatingInputGroup">Username</label>
    <input type="text" class="form-input" id="floatingInputGroup" placeholder="Username">
  </div>
</div>

Always place form helps and form feedbacks outside of the .input-group container to ensure proper styling and positioning.

@
Please choose a username.
HTML
<div>
  <div class="input-group">
    <span class="input-addon">@</span>
    <div class="form-floating">
      <label for="floatingInputGroupInvalid">Username</label>
      <input type="text" class="form-input is-invalid" id="floatingInputGroupInvalid" placeholder="Username" required>
    </div>
  </div>
  <div class="invalid-feedback">
    Please choose a username.
  </div>
</div>

Icons alongside floating labels use .input-addon elements, the same as any other input group.

HTML
<div class="input-group">
  <div class="form-floating">
    <label for="floatingInputGroupUsername">Username</label>
    <input type="text" class="form-input" id="floatingInputGroupUsername" placeholder="Username" required>
  </div>
  <div class="form-floating">
    <label for="floatingInputGroupPassword">Password</label>
    <input type="password" autocomplete="current-password" class="form-input" id="floatingInputGroupPassword" placeholder="Password" required>
  </div>
  <button type="button" class="input-addon" aria-label="Show password">
    <svg class="icon" aria-hidden="true"><use href="#eye-solid"></use></svg>
  </button>
</div>

See the input group page for the full set of addon types and layout options.

Grid layout

Combine .form-floating containers with Chassis CSS's grid system by placing them within column classes:

HTML
<div class="row g-medium">
  <div class="medium:col">
    <div class="form-floating">
      <label for="floatingInputGrid">Email address</label>
      <input type="email" class="form-input" id="floatingInputGrid" placeholder="name@example.com" value="user@chassis.css">
    </div>
  </div>
  <div class="medium:col">
    <div class="form-floating">
      <label for="floatingSelectGrid">Preferences</label>
      <select class="form-input" id="floatingSelectGrid">
        <option selected>Choose an option</option>
        <option value="1">Option One</option>
        <option value="2">Option Two</option>
        <option value="3">Option Three</option>
      </select>
    </div>
  </div>
</div>

See the forms layout page for the full grid, horizontal, and inline form patterns.

CSS

The Floating label component can be customized at both runtime (via custom properties) and compile time (via Sass variables).

Custom properties

The Floating label component exposes CSS custom properties to control its appearance at runtime.

--height: var(--form-floating-height, #{$form-floating-height});
--inactive-padding-y: var(--form-floating-inactive-padding-y, #{$form-floating-inactive-padding-y});
--active-padding-t: var(--form-floating-active-padding-t, #{$form-floating-active-padding-t});
--active-padding-b: var(--form-floating-active-padding-b, #{$form-floating-active-padding-b});
--input-padding-x: var(--form-floating-input-padding-x, #{$form-floating-input-padding-x});
--border-bottom-width: var(--form-floating-border-width, #{$form-floating-border-width});
--border-radius-t: var(--form-floating-border-radius-t, #{$form-floating-border-radius-t});
--border-radius-b: var(--form-floating-border-radius-b, #{$form-floating-border-radius-b});
--label-height: var(--form-floating-label-height, #{$form-floating-label-height});
--label-padding-y: var(--form-floating-label-padding-y, #{$form-floating-label-padding-y});
--label-transform: var(--form-floating-label-transform, #{$form-floating-label-transform});
--transition: var(--form-floating-transition, #{$form-floating-transition});

Check the overview page to see the CSS variables shared by all form elements.

Sass variables

The Floating label component uses Sass variables in scss/config/_defaults.scss to define its defaults; they are also exposed as CSS custom properties using the --cx- prefix for runtime override — $variable-name becomes --cx-variable-name. See the component anatomy page.

$form-floating-height:                      3.5rem;
$form-floating-input-padding-x:             $form-medium-padding-x;
$form-floating-inactive-padding-y:          1rem;
$form-floating-active-padding-t:            1.5rem;
$form-floating-active-padding-b:            .5rem;
$form-floating-label-padding-y:             $form-floating-inactive-padding-y;
$form-floating-label-height:                1.5em;
$form-floating-label-transform:             scale(.85) translateY(-.5rem) translateX(.15rem);
$form-floating-transition:                  opacity .1s ease-in-out, transform .1s ease-in-out;

Check the overview page to see the Sass variables shared by all form elements.

Design tokens

Check the overview page to see the design tokens shared by all form elements.