04 / 16
All 16 in 16 Tailwind CSS Floating Label Inputs
- 01Tailwind Staggered Letter-Cascade Floating Label
- 02Tailwind Gradient-Ink Floating Label with Animated Underline Draw
- 03Tailwind Conic Border-Beam Floating Label (@property Animation)
- 04Pure Tailwind Floating Label Input (No JavaScript)
- 05Tailwind Floating Label with Chrome Autofill Fix
- 06Tailwind Floating Label Validation States (Error + Success)
- 07Accessible Tailwind Floating Label Input (ARIA + WCAG)
- 08Tailwind Floating Label for Select Dropdowns
- 09Tailwind Material Design Outlined Text Field
- 10Tailwind Filled & Underline Floating Label Input
- 11Tailwind Dark Mode Floating Label Input with light-dark()
- 12Tailwind Input with Icon Inside and Floating Label
- 13Responsive Tailwind Floating Label for Mobile (No iOS Zoom)
- 14Tailwind Auto-Growing Textarea with Floating Label
- 15Tailwind Password Input with Show/Hide Toggle and Floating Label
- 16Tailwind Multi-Column Form Grid with Floating Labels
Pure Tailwind Floating Label Input (No JavaScript)
The canonical floating label in its native Tailwind form: peer on the input, and peer-focus: plus peer-[:not(:placeholder-shown)]: on the label. The label floats with translate-y-[calc(-50%_-_13px)] scale-72 — transform-only, zero listeners, zero hydration. Every other demo in the collection builds on this one.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tfl-04 w-full min-h-svh grid place-items-center bg-tfl04-bg px-5 py-12 font-tfl04-sans" aria-label="Pure Tailwind floating label demo">
<form class="flex w-full max-w-[380px] flex-col gap-5 rounded-3xl bg-white p-8 shadow-[0_24px_60px_-30px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl04-ink/5" novalidate>
<div class="mb-1 grid gap-1">
<h3 class="text-2xl font-extrabold tracking-[-0.02em] text-tfl04-ink">Sign in</h3>
<p class="text-sm text-tfl04-mut">Welcome back — pick up where you left off.</p>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="email" id="tfl-04-email" name="email" placeholder=" " autocomplete="email">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-email">Email address</label>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="text" id="tfl-04-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-name">Full name</label>
</div>
<button class="min-h-12 cursor-pointer rounded-xl bg-tfl04-acc text-[15px] font-bold text-white shadow-[0_10px_24px_-12px_var(--color-tfl04-acc)] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl04-acc/50 motion-reduce:transition-none" type="button">Continue</button>
</form>
</section><section class="tfl-04 w-full min-h-svh grid place-items-center bg-tfl04-bg px-5 py-12 font-tfl04-sans" aria-label="Pure Tailwind floating label demo">
<form class="flex w-full max-w-[380px] flex-col gap-5 rounded-3xl bg-white p-8 shadow-[0_24px_60px_-30px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl04-ink/5" novalidate>
<div class="mb-1 grid gap-1">
<h3 class="text-2xl font-extrabold tracking-[-0.02em] text-tfl04-ink">Sign in</h3>
<p class="text-sm text-tfl04-mut">Welcome back — pick up where you left off.</p>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="email" id="tfl-04-email" name="email" placeholder=" " autocomplete="email">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-email">Email address</label>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="text" id="tfl-04-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-name">Full name</label>
</div>
<button class="min-h-12 cursor-pointer rounded-xl bg-tfl04-acc text-[15px] font-bold text-white shadow-[0_10px_24px_-12px_var(--color-tfl04-acc)] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl04-acc/50 motion-reduce:transition-none" type="button">Continue</button>
</form>
</section>@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400..800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400..800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Four sibling-combinator rules became four
peer-*utilities on the label; the--radiuscustom property became plainrounded-xl. - The two-value offset (
translate:0 -50%+translateY(-13px)) folded into onetranslate-y-[calc(-50%_-_13px)]. - UI upgrade from the CSS original: the resting label rose from
#7a7f93(4.1:1) tooklch(0.5)(5.9:1); the accent deepened so the floated 11.5px label passes AA. - UI upgrade: a sub-heading, a tinted button shadow and Plus Jakarta Sans;
outline-hiddeninstead ofoutline:nonekeeps a focus indicator in forced-colors mode.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Pure Tailwind Floating Label Input (No JavaScript)
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/pure-tailwind-floating-label-input-no-javascript/
The canonical floating label in its native Tailwind form: peer on the input, and peer-focus: plus peer-[:not(:placeholder-shown)]: on the label. The label floats with translate-y-[calc(-50%_-_13px)] scale-72 — transform-only, zero listeners, zero hydration. Every other demo in the collection builds on this one.
## HTML
```html
<section class="tfl-04 w-full min-h-svh grid place-items-center bg-tfl04-bg px-5 py-12 font-tfl04-sans" aria-label="Pure Tailwind floating label demo">
<form class="flex w-full max-w-[380px] flex-col gap-5 rounded-3xl bg-white p-8 shadow-[0_24px_60px_-30px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl04-ink/5" novalidate>
<div class="mb-1 grid gap-1">
<h3 class="text-2xl font-extrabold tracking-[-0.02em] text-tfl04-ink">Sign in</h3>
<p class="text-sm text-tfl04-mut">Welcome back — pick up where you left off.</p>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="email" id="tfl-04-email" name="email" placeholder=" " autocomplete="email">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-email">Email address</label>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="text" id="tfl-04-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-name">Full name</label>
</div>
<button class="min-h-12 cursor-pointer rounded-xl bg-tfl04-acc text-[15px] font-bold text-white shadow-[0_10px_24px_-12px_var(--color-tfl04-acc)] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl04-acc/50 motion-reduce:transition-none" type="button">Continue</button>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Pure Tailwind Floating Label Input (No JavaScript)
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/pure-tailwind-floating-label-input-no-javascript/
The canonical floating label in its native Tailwind form: peer on the input, and peer-focus: plus peer-[:not(:placeholder-shown)]: on the label. The label floats with translate-y-[calc(-50%_-_13px)] scale-72 — transform-only, zero listeners, zero hydration. Every other demo in the collection builds on this one.
## HTML
```html
<section class="tfl-04 w-full min-h-svh grid place-items-center bg-tfl04-bg px-5 py-12 font-tfl04-sans" aria-label="Pure Tailwind floating label demo">
<form class="flex w-full max-w-[380px] flex-col gap-5 rounded-3xl bg-white p-8 shadow-[0_24px_60px_-30px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl04-ink/5" novalidate>
<div class="mb-1 grid gap-1">
<h3 class="text-2xl font-extrabold tracking-[-0.02em] text-tfl04-ink">Sign in</h3>
<p class="text-sm text-tfl04-mut">Welcome back — pick up where you left off.</p>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="email" id="tfl-04-email" name="email" placeholder=" " autocomplete="email">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-email">Email address</label>
</div>
<div class="relative">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl04-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl04-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl04-line-hover focus:border-tfl04-acc focus:ring-4 focus:ring-tfl04-acc/15 motion-reduce:transition-none" type="text" id="tfl-04-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 origin-left -translate-y-1/2 text-base text-tfl04-mut transition-[translate,scale,color] duration-200 ease-out peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 peer-focus:text-tfl04-acc peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_13px)] peer-[:not(:placeholder-shown)]:scale-72 motion-reduce:transition-none" for="tfl-04-name">Full name</label>
</div>
<button class="min-h-12 cursor-pointer rounded-xl bg-tfl04-acc text-[15px] font-bold text-white shadow-[0_10px_24px_-12px_var(--color-tfl04-acc)] transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl04-acc/50 motion-reduce:transition-none" type="button">Continue</button>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl04-bg: #f2f3f7;
--color-tfl04-bg: oklch(0.96 0.006 270);
--color-tfl04-ink: #191c2b;
--color-tfl04-ink: oklch(0.22 0.03 270);
--color-tfl04-mut: #62677c;
--color-tfl04-mut: oklch(0.5 0.03 270);
--color-tfl04-line: #d6d9e4;
--color-tfl04-line: oklch(0.88 0.015 270);
--color-tfl04-line-hover: #b4b9cc;
--color-tfl04-line-hover: oklch(0.78 0.025 270);
--color-tfl04-acc: #4a50d4;
--color-tfl04-acc: oklch(0.5 0.2 272);
--font-tfl04-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tfl-04 {
width: 100%;
min-height: 100svh;
}
```How this works
The label must come after the input. peer-* compiles to the general sibling combinator (.peer:focus ~ label), which only looks forward. Visually the label sits on top via absolute; in source it follows.
<input class="peer …" placeholder=" ">
<label class="absolute top-1/2 -translate-y-1/2 origin-left
peer-focus:translate-y-[calc(-50%_-_13px)] peer-focus:scale-72 …">Why the arbitrary :not(). peer-placeholder-shown: styles the resting state; floating on not shown is clearer to read and lets the resting classes be the defaults.
Make it yours
- Float height/shrink:
13pxandscale-72. - Accent:
--color-tfl04-accdrives border, ring, label and button. - Roundness: swap
rounded-xlon input and button together. - Speed:
duration-200on the label.
Gotchas — read before shipping
- The placeholder must be a single space — an empty placeholder makes
:placeholder-shownunreliable. - Keep
origin-leftor the scaled label drifts toward centre. - Never drop the
<label>for a placeholder; placeholders vanish on input and fail WCAG 3.3.2.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:placeholder-shown is universal; the floor is Tailwind v4's generated sheet.