SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/floating-label-inputs

7 CSS Floating Label Inputs

6 Pure CSS1 with JSPublished 2026-10-05Free · no sign-up

Seven floating label fields where the label starts inside the field and moves up when you type: a classic version, an outlined field with a notch, a minimal underline, a filled field, a textarea, a select and a field that shows its error only after you have interacted with it.

01. Classic Floating Label

InputLabel

The label sits inside the empty field and moves up and shrinks when the field is focused or filled. The input carries placeholder=" " so :placeholder-shown tells the CSS whether it is empty; the label is still a real label element linked with for and id.

View code
<div class="fl1"><input id="fl1e" type="email" placeholder=" " autocomplete="email"><label for="fl1e">Email address</label></div>
.fl1 { position: relative; width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl1 input { width: 100%; box-sizing: border-box; padding: 24px 14px 8px; border: 1px solid #98a2b3; border-radius: 10px; background: #fff; color: #101828; font: 16px Inter, system-ui, sans-serif; }
.fl1 label { position: absolute; left: 15px; top: 17px; color: #475467; font-size: 16px; pointer-events: none; transform-origin: left top; transition: transform .18s ease, color .18s; }
.fl1 input:focus + label, .fl1 input:not(:placeholder-shown) + label { transform: translateY(-10px) scale(.78); }
.fl1 input:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.2); }
.fl1 input:focus + label { color: #1d4ed8; }
@media (prefers-reduced-motion: reduce) { .fl1 label { transition: none; } }

02. Outlined Field with Notched Label

InputMaterial

In the outlined style the label floats onto the border and a background patch behind it cuts a notch in the outline. The patch colour must match the page, which is the one limitation; the other demos avoid it if your background varies.

View code
<div class="fl2"><input id="fl2n" placeholder=" " autocomplete="name"><label for="fl2n">Full name</label></div>
.fl2 { position: relative; width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl2 input { width: 100%; box-sizing: border-box; padding: 16px 14px; border: 1px solid #98a2b3; border-radius: 8px; background: transparent; color: #101828; font: 16px Inter, system-ui, sans-serif; }
.fl2 label { position: absolute; left: 10px; top: 15px; padding: 0 5px; background: #f3f5f9; color: #475467; font-size: 16px; line-height: 1.2; pointer-events: none; transition: top .18s, font-size .18s, color .18s; }
.fl2 input:focus + label, .fl2 input:not(:placeholder-shown) + label { top: -8px; font-size: 12px; }
.fl2 input:focus { outline: none; border: 2px solid #6941c6; padding: 15px 13px; }
.fl2 input:focus + label { color: #6941c6; }
@media (prefers-reduced-motion: reduce) { .fl2 label { transition: none; } }

03. Underline Field with Growing Bar

InputMinimal

A minimal field with only a bottom border. On focus a coloured bar grows from the centre across the underline, and the label floats up. The bar is a sibling span scaled with transform, so it animates smoothly without changing the layout.

View code
<div class="fl3"><input id="fl3c" placeholder=" " autocomplete="address-level2"><label for="fl3c">City</label><span aria-hidden="true"></span></div>
.fl3 { position: relative; width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl3 input { width: 100%; box-sizing: border-box; padding: 22px 0 8px; border: 0; border-bottom: 1px solid #475569; background: transparent; color: #f8fafc; font: 16px Inter, system-ui, sans-serif; }
.fl3 input:focus { outline: none; }
.fl3 label { position: absolute; left: 0; top: 20px; color: #94a3b8; font-size: 16px; pointer-events: none; transition: top .18s, font-size .18s, color .18s; }
.fl3 input:focus + label, .fl3 input:not(:placeholder-shown) + label { top: 0; font-size: 12px; }
.fl3 input:focus + label { color: #2dd4bf; }
.fl3 span { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: #2dd4bf; transform: scaleX(0); transition: transform .25s ease; }
.fl3 input:focus ~ span { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) { .fl3 label, .fl3 span { transition: none; } }

04. Filled Field Style

InputMaterial

The filled style gives the field a tinted background and a bottom border only, which makes it easy to spot on a busy page. The label floats within the fill, so there is no notch to worry about and any page background works.

View code
<div class="fl4"><input id="fl4p" type="tel" placeholder=" " autocomplete="tel"><label for="fl4p">Phone number</label></div>
.fl4 { position: relative; width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl4 input { width: 100%; box-sizing: border-box; padding: 24px 14px 8px; border: 0; border-bottom: 2px solid #667085; border-radius: 8px 8px 0 0; background: #eaecf0; color: #101828; font: 16px Inter, system-ui, sans-serif; transition: background .2s; }
.fl4 input:hover { background: #e4e7ec; }
.fl4 input:focus { outline: none; border-bottom-color: #0e7090; background: #e0f2fe; }
.fl4 label { position: absolute; left: 14px; top: 16px; color: #475467; font-size: 16px; pointer-events: none; transition: top .18s, font-size .18s, color .18s; }
.fl4 input:focus + label, .fl4 input:not(:placeholder-shown) + label { top: 6px; font-size: 12px; }
.fl4 input:focus + label { color: #0e7090; }
@media (prefers-reduced-motion: reduce) { .fl4 label, .fl4 input { transition: none; } }

05. Floating Label on a Textarea

TextareaLabel

The same technique works on a textarea: placeholder=" " plus :placeholder-shown. The label floats to the top edge, and a solid background behind it keeps it readable once the text scrolls underneath.

View code
<div class="fl5"><textarea id="fl5m" rows="4" placeholder=" "></textarea><label for="fl5m">Your message</label></div>
.fl5 { position: relative; width: 300px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl5 textarea { display: block; width: 100%; box-sizing: border-box; padding: 26px 14px 10px; border: 1px solid #98a2b3; border-radius: 10px; background: #fff; color: #101828; font: 15px/1.5 Inter, system-ui, sans-serif; resize: vertical; }
.fl5 label { position: absolute; left: 1px; right: 16px; top: 1px; padding: 14px 14px 4px; border-radius: 10px 0 0 0; background: #fff; color: #475467; font-size: 15px; pointer-events: none; transition: padding .18s, font-size .18s, color .18s; }
.fl5 textarea:focus + label, .fl5 textarea:not(:placeholder-shown) + label { padding-top: 6px; font-size: 12px; }
.fl5 textarea:focus { outline: none; border-color: #0f766e; box-shadow: 0 0 0 3px rgba(15,118,110,.2); }
.fl5 textarea:focus + label { color: #0f766e; }
@media (prefers-reduced-motion: reduce) { .fl5 label { transition: none; } }

06. Select with a Floating Label

SelectLabel

A select has no placeholder, so the label floats when the select is :valid. The first option is an empty, disabled prompt and the select is required, which makes it invalid until something real is chosen.

View code
<div class="fl6"><select id="fl6s" required><option value="" selected disabled></option><option>Small</option><option>Medium</option><option>Large</option></select><label for="fl6s">Size</label></div>
.fl6 { position: relative; width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl6 select { width: 100%; box-sizing: border-box; padding: 24px 40px 8px 14px; border: 1px solid #475569; border-radius: 10px; background: #111827 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 12px center / 18px; color: #f8fafc; font: 16px Inter, system-ui, sans-serif; appearance: none; }
.fl6 label { position: absolute; left: 15px; top: 17px; color: #94a3b8; font-size: 16px; pointer-events: none; transition: top .18s, font-size .18s, color .18s; }
.fl6 select:focus + label, .fl6 select:valid + label { top: 6px; font-size: 12px; }
.fl6 select:focus { outline: none; border-color: #a78bfa; box-shadow: 0 0 0 3px rgba(167,139,250,.25); }
.fl6 select:focus + label { color: #c4b5fd; }
@media (prefers-reduced-motion: reduce) { .fl6 label { transition: none; } }

07. Floating Label with Validation

InputValidation

Errors show only after the person has interacted with the field, using :user-invalid, so nobody sees red before typing. The error text is linked with aria-describedby and the input gets aria-invalid from a few lines of script, keeping the state available to screen readers.

View code
<div class="fl7"><div class="f"><input id="fl7u" placeholder=" " required minlength="3" aria-describedby="fl7h"><label for="fl7u">Username</label></div><p id="fl7h" class="h">At least 3 characters.</p></div>
.fl7 { width: 280px; max-width: 100%; font-family: Inter, system-ui, sans-serif; }
.fl7 .f { position: relative; }
.fl7 input { width: 100%; box-sizing: border-box; padding: 24px 14px 8px; border: 1px solid #98a2b3; border-radius: 10px; background: #fff; color: #101828; font: 16px Inter, system-ui, sans-serif; }
.fl7 label { position: absolute; left: 15px; top: 17px; color: #475467; font-size: 16px; pointer-events: none; transition: top .18s, font-size .18s; }
.fl7 input:focus + label, .fl7 input:not(:placeholder-shown) + label { top: 6px; font-size: 12px; }
.fl7 input:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37,99,235,.2); }
.fl7 .h { margin: 6px 2px 0; color: #475467; font-size: 13px; }
.fl7 input:user-invalid { border-color: #d92d20; }
.fl7 input:user-invalid:focus { box-shadow: 0 0 0 3px rgba(217,45,32,.2); }
.fl7 .f:has(input:user-invalid) + .h { color: #b42318; font-weight: 600; }
.fl7 .f:has(input:user-invalid) + .h::before { content: "⚠ "; }
@media (prefers-reduced-motion: reduce) { .fl7 label { transition: none; } }
var i = document.getElementById('fl7u');
['input', 'blur'].forEach(function (ev) { i.addEventListener(ev, function () { if (ev === 'blur' || i.getAttribute('aria-invalid')) i.setAttribute('aria-invalid', !i.checkValidity()); }); });

About these CSS Floating Label Inputs

The placeholder trick

CSS cannot tell whether an input has a value, but it can tell whether its placeholder is showing. Give the input placeholder=" " (a single space) and use :not(:placeholder-shown) to float the label once something is typed.

Keep a real label

The floating text must be a <label> linked to the input with for and id, not a placeholder. Placeholders vanish when typing starts and are often too pale to read.

Selects float when valid

A select has no placeholder. Make the first option empty and disabled and the select required; it is then :invalid until a real choice is made, and :valid floats the label.

Errors after interaction

:user-invalid only matches once the person has edited or left a field, so nobody sees red on page load. Link the hint with aria-describedby and set aria-invalid so the error is announced.

Frequently asked questions

Do floating labels hurt usability?

The label stays visible at all times, which is the main thing. Keep the floated label large enough to read, at least 12px.

Do they work with autofill?

Yes. Autofilled inputs no longer show their placeholder, so the label floats as soon as the browser fills them.

Why does the outlined style need a background colour?

The notch is a patch of background behind the label covering the border; on a varied background use the filled or classic style.