Design System · Input Components

Floating Label
Pattern

A reusable input field component following Material Design 3 guidelines — with full state behavior, token mapping, animation specification, and accessibility requirements.

Component Text Field
Variant Filled (Floating Label)
MD Spec Material Design 3
WCAG 2.1 AA Compliant
1
Component States
All interactive and non-interactive states for the filled text field variant
Default (Empty)
Supporting text
Focused
Label floats above · Cursor active
Filled (has value)
Label stays above · No focus ring
Error
Full name is required
Disabled
Not available

Try the live fields above — click any field to see the label animate from inside the input to above it. The label stays floated as long as a value is present, even after losing focus.

2
Anatomy
Structural parts of the filled text field component
Name John Smith Supporting text 1 2 3 4 5 56dp
1
Container
Filled surface background. Uses surface-variant color with top-left and top-right radius of 4dp. Height is always 56dp. Bottom edge has a 1dp indicator line.
2
Floating Label
Animates between two positions: resting (body-lg, 16sp, centered in container) and floating (body-sm, 12sp, top of container at 6dp from top edge). Color shifts from on-surface-variantprimary on focus.
3
Input Text
body-lg (16sp). Color: on-surface. Left padding 16dp, right padding 16dp. Sits in lower portion of the container when label is floating.
4
Active Indicator
Bottom border of container. 1dp when inactive (outline color), 2dp when focused (primary color) or in error state (error color).
5
Supporting Text
body-sm (12sp), 4dp below the container bottom. Color: on-surface-variant or error. Used for hints, character count, or validation messages.
3
Design Tokens
MD3 color roles and typescale tokens applied to each component part
Color Tokens
TokenRoleHex
md.sys.color.primaryLabel (focused), active indicator (focused)#1A6EBD
md.sys.color.on-surface-variantLabel (resting), supporting text, placeholder#42474E
md.sys.color.on-surfaceInput text (typed value)#1A1C20
md.sys.color.surface-variantContainer fill (default, focused)#DDE3EA
md.sys.color.outlineActive indicator (default state, 1dp)#72787E
md.sys.color.errorLabel (error), active indicator (error), error text#BA1A1A
md.sys.color.on-surface @38%All elements (disabled state, opacity overlay)38% opacity
Typescale Tokens
TokenApplied toValue
md.sys.typescale.body-largeInput text, label (resting state)16sp / 400 / Roboto
md.sys.typescale.body-smallLabel (floating state), supporting text12sp / 500 / Roboto
md.sys.typescale.body-mediumPlaceholder text14sp / 400 / Roboto
Spacing Tokens
TokenApplied toValue
Container heightText field container56dp
Horizontal paddingLabel, input text left/right16dp
Label top (floating)Floated label top offset6dp from top
Support text gapContainer bottom → support text4dp
Shape (top corners)Container top-left, top-right radius4dp
Active indicator (default)Bottom border, inactive1dp
Active indicator (focused)Bottom border, focused/error2dp
4
State Matrix
Precise property values for each field state
State Label position Label color Label size Container fill Indicator Support text
Default Inside field, vertically centered on-surface-variant 16sp / 400 surface-variant 1dp / outline on-surface-variant
Focused Above field · top: 6dp primary 12sp / 500 surface-variant + state 2dp / primary on-surface-variant
Filled Above field · top: 6dp on-surface-variant 12sp / 500 surface-variant 1dp / outline on-surface-variant
Error Above field · top: 6dp error 12sp / 500 surface-variant 2dp / error error
Disabled Inside field, vertically centered on-surface @38% 16sp / 400 on-surface @4% 1dp @38% on-surface @38%
5
Motion Specification
MD3 motion tokens — Emphasized easing for label float animation

Label float (resting → floating)

time value
Duration200ms
MD3 tokenmotion.duration.short4
Easingcubic-bezier(.2, 0, 0, 1)
MD3 easingmotion.easing.emphasized
Properties animatedtop, font-size, color, transform

Label return (floating → resting)

time
Duration150ms
MD3 tokenmotion.duration.short3
Easingcubic-bezier(.4, 0, .6, 1)
MD3 easingmotion.easing.emphasized-decelerate
Triggerblur + :placeholder-shown (no value)

Reduced motion override

Media queryprefers-reduced-motion: reduce
Duration override0ms (instant swap)
Transition overridenone
WCAG referenceSuccess Criterion 2.3.3
BehaviorLabel snaps — no animation

Active indicator transition

Propertyborder-bottom-width, border-color
In duration200ms
Out duration150ms
Easing (both)cubic-bezier(.2, 0, 0, 1)
NoteSynchronize with label animation
6
Usage Guidance
Do and Don't patterns for implementing this component
Do — Float label above, outside the input
Required field

The label animates above the container boundary when focused or filled. The typed value is fully visible with no overlap. The label uses the reserved space above the input created by the wrapper's top padding.

Don't — Leave label overlapping typed text
Full Name

Never leave the label at its resting position when a value has been entered. The label sits on top of the typed text, making it unreadable and creating an inaccessible, visually broken state.

Do — Use placeholder=" " (single space) as the CSS hook
<input type="text"
  placeholder=" "
  id="name-field" />

input:not(:placeholder-shown) + label {
  top: 6dp;
  font-size: 12sp;
}

A single space placeholder enables the :not(:placeholder-shown) CSS selector — the label stays floated when the field has a value, even after losing focus. No JavaScript needed.

Don't — Use a <span> instead of <label>
/* ✗ Never do this */
<span class="label">Name</span>
<input type="text" />

/* ✓ Always use this */
<label for="name-field">Name</label>
<input type="text" id="name-field" />

A <span> is not announced by screen readers as a form label. Always use a semantic <label> with a matching for/id pair. This is a WCAG 2.1 Level A requirement.

7
Reference Implementation
Production-ready CSS for the filled text field with floating label
floating-label.css — MD3 Filled Text Field CSS
/* ── Wrapper — reserves space above input ─────────────── */
.field-wrap {
  position: relative;
  padding-top: 20px;          /* reserved zone for floated label */
}

/* ── Container ────────────────────────────────────────── */
.field-wrap input {
  width: 100%;
  height: 56px;              /* MD3 fixed container height    */
  background: var(--md-surface-variant);
  border: none;
  border-bottom: 1px solid var(--md-outline);
  border-radius: 4px 4px 0 0;  /* top corners only per MD3     */
  padding: 8px 16px;
  font: var(--md-typescale-body-large);
  color: var(--md-on-surface);
  outline: none;
  transition: border-color 200ms cubic-bezier(.2, 0, 0, 1);
}

.field-wrap input:focus {
  border-bottom: 2px solid var(--md-primary);
}

/* ── Label ────────────────────────────────────────────── */
.field-wrap label {
  position: absolute;
  left: 16px;
  top: 50%;                  /* vertically centered in field  */
  transform: translateY(-50%);
  font-size: 16px;             /* body-large                   */
  color: var(--md-on-surface-variant);
  pointer-events: none;
  transition:
    top 200ms cubic-bezier(.2, 0, 0, 1),
    font-size 200ms cubic-bezier(.2, 0, 0, 1),
    color 200ms cubic-bezier(.2, 0, 0, 1),
    transform 200ms cubic-bezier(.2, 0, 0, 1);
}

/* ── Floating state — :focus OR has a value ───────────── */
.field-wrap input:focus + label,
.field-wrap input:not(:placeholder-shown) + label {
  top: 0;                    /* floats into wrapper top zone  */
  transform: none;
  font-size: 12px;             /* body-small                   */
  font-weight: 500;
  color: var(--md-on-surface-variant);
  left: 2px;                  /* slight left adjust when small */
}

.field-wrap input:focus + label {
  color: var(--md-primary);    /* primary color only when focused */
}

/* ── Error state ──────────────────────────────────────── */
.field-wrap.error input  { border-bottom: 2px solid var(--md-error); }
.field-wrap.error label  { color: var(--md-error); }

/* ── Disabled state ───────────────────────────────────── */
.field-wrap input:disabled {
  background: rgba(28,27,31,.04);
  border-bottom-color: rgba(28,27,31,.38);
  color: rgba(28,27,31,.38);
}
.field-wrap input:disabled + label { color: rgba(28,27,31,.38); }

/* ── Accessibility: reduced motion ────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .field-wrap label { transition: none; }
  .field-wrap input { transition: none; }
}
8
Accessibility Requirements
WCAG 2.1 Level AA compliance checklist

Color contrast

Floated label color primary (#1A6EBD) on surface-variant background yields 4.8:1. Resting label yields 4.6:1. Both pass AA for normal text.

primary: 4.8:1 ✓ resting: 4.6:1 ✓

Semantic HTML

Always use a native <label for="..."> element paired with the input's id. Screen readers announce the label when the input receives focus, regardless of its visual position.

Reduced motion

Wrap all transitions in @media (prefers-reduced-motion: reduce) and set transition: none. Required for WCAG 2.3.3 (Animation from Interactions).

Focus indicator

The active indicator (2dp border-bottom) plus label color change together serve as the focus indicator. Ensure the indicator color primary has 3:1 contrast against adjacent surfaces per WCAG 1.4.11.

No placeholder text

Do not use visible placeholder text when a floating label is present — this doubles the label and fails 1.3.1 (Info and Relationships). Use placeholder=" " (single space) only as a CSS selector hook.

Error announcement

Add aria-describedby on the input pointing to the supporting text element. Add aria-invalid="true" in error state so screen readers surface the validation message automatically.