16 / 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
Tailwind Multi-Column Form Grid with Floating Labels
A checkout address form laying floating-label fields onto a six-track grid: col-span-3 pairs, a full-width street, and a col-span-2 City/State/ZIP row. Named container queries (@container/form + @max-[26rem]/form:) re-flow it by the form's own width, and floated labels snap to uppercase micro-legends that unify the grid.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tfl-16 w-full min-h-svh grid place-items-center bg-tfl16-bg px-5 py-12 font-tfl16-sans" aria-label="Multi-column form grid floating labels demo">
<form class="@container/form flex w-full max-w-[560px] flex-col gap-[22px] rounded-3xl bg-white p-8 shadow-[0_24px_60px_-32px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl16-ink/5" novalidate>
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="text-2xl font-bold tracking-[-0.02em] text-tfl16-ink">Shipping address</h3>
<span class="text-xs font-semibold tracking-[.12em] text-tfl16-mut uppercase">Step 1 of 2</span>
</div>
<div class="grid grid-cols-6 gap-4">
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-first" name="given-name" placeholder=" " autocomplete="given-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-first">First name</label>
</div>
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-last" name="family-name" placeholder=" " autocomplete="family-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-last">Last name</label>
</div>
<div class="relative col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-street" name="street-address" placeholder=" " autocomplete="street-address">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-street">Street address</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-city" name="address-level2" placeholder=" " autocomplete="address-level2">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-city">City</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-state" name="address-level1" placeholder=" " autocomplete="address-level1">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-state">State</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink tabular-nums outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-zip">ZIP</label>
</div>
</div>
<button class="min-h-[52px] cursor-pointer rounded-xl bg-tfl16-acc text-[15px] font-semibold text-white shadow-[0_10px_24px_-12px_var(--color-tfl16-acc)] transition-[filter,scale] duration-150 hover:brightness-115 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl16-acc/50" type="button">Continue to payment</button>
</form>
</section><section class="tfl-16 w-full min-h-svh grid place-items-center bg-tfl16-bg px-5 py-12 font-tfl16-sans" aria-label="Multi-column form grid floating labels demo">
<form class="@container/form flex w-full max-w-[560px] flex-col gap-[22px] rounded-3xl bg-white p-8 shadow-[0_24px_60px_-32px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl16-ink/5" novalidate>
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="text-2xl font-bold tracking-[-0.02em] text-tfl16-ink">Shipping address</h3>
<span class="text-xs font-semibold tracking-[.12em] text-tfl16-mut uppercase">Step 1 of 2</span>
</div>
<div class="grid grid-cols-6 gap-4">
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-first" name="given-name" placeholder=" " autocomplete="given-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-first">First name</label>
</div>
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-last" name="family-name" placeholder=" " autocomplete="family-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-last">Last name</label>
</div>
<div class="relative col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-street" name="street-address" placeholder=" " autocomplete="street-address">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-street">Street address</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-city" name="address-level2" placeholder=" " autocomplete="address-level2">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-city">City</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-state" name="address-level1" placeholder=" " autocomplete="address-level1">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-state">State</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink tabular-nums outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-zip">ZIP</label>
</div>
</div>
<button class="min-h-[52px] cursor-pointer rounded-xl bg-tfl16-acc text-[15px] font-semibold text-white shadow-[0_10px_24px_-12px_var(--color-tfl16-acc)] transition-[filter,scale] duration-150 hover:brightness-115 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl16-acc/50" type="button">Continue to payment</button>
</form>
</section>@import "tailwindcss";
@theme {
--color-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
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-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
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=IBM+Plex+Sans:wght@400;500;600;700&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=IBM+Plex+Sans:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Per-field
style="--span:3"variables became plaincol-span-*utilities; the named@container flb16formbecame@container/formwith@max-[…]/form:variants. - The ellipsis guard (
overflow/text-overflow/white-space) became onetruncate. - UI upgrade — bug fix: the original collapsed below a 559px container, but container queries measure the content box — a 560px form with 32px padding is 496px inside, so it was single-column at every size. The thresholds now sit at 26rem/32rem of content width.
- UI upgrade: a middle breakpoint (City full-width, State + ZIP paired) avoids cramped thirds, the header gained a step indicator, and IBM Plex Sans replaces Segoe UI.
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: Tailwind Multi-Column Form Grid with Floating Labels
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/tailwind-multi-column-form-grid-floating-labels/
A checkout address form laying floating-label fields onto a six-track grid: col-span-3 pairs, a full-width street, and a col-span-2 City/State/ZIP row. Named container queries (@container/form + @max-[26rem]/form:) re-flow it by the form's own width, and floated labels snap to uppercase micro-legends that unify the grid.
## HTML
```html
<section class="tfl-16 w-full min-h-svh grid place-items-center bg-tfl16-bg px-5 py-12 font-tfl16-sans" aria-label="Multi-column form grid floating labels demo">
<form class="@container/form flex w-full max-w-[560px] flex-col gap-[22px] rounded-3xl bg-white p-8 shadow-[0_24px_60px_-32px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl16-ink/5" novalidate>
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="text-2xl font-bold tracking-[-0.02em] text-tfl16-ink">Shipping address</h3>
<span class="text-xs font-semibold tracking-[.12em] text-tfl16-mut uppercase">Step 1 of 2</span>
</div>
<div class="grid grid-cols-6 gap-4">
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-first" name="given-name" placeholder=" " autocomplete="given-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-first">First name</label>
</div>
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-last" name="family-name" placeholder=" " autocomplete="family-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-last">Last name</label>
</div>
<div class="relative col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-street" name="street-address" placeholder=" " autocomplete="street-address">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-street">Street address</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-city" name="address-level2" placeholder=" " autocomplete="address-level2">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-city">City</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-state" name="address-level1" placeholder=" " autocomplete="address-level1">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-state">State</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink tabular-nums outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-zip">ZIP</label>
</div>
</div>
<button class="min-h-[52px] cursor-pointer rounded-xl bg-tfl16-acc text-[15px] font-semibold text-white shadow-[0_10px_24px_-12px_var(--color-tfl16-acc)] transition-[filter,scale] duration-150 hover:brightness-115 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl16-acc/50" type="button">Continue to payment</button>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
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: Tailwind Multi-Column Form Grid with Floating Labels
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/tailwind-multi-column-form-grid-floating-labels/
A checkout address form laying floating-label fields onto a six-track grid: col-span-3 pairs, a full-width street, and a col-span-2 City/State/ZIP row. Named container queries (@container/form + @max-[26rem]/form:) re-flow it by the form's own width, and floated labels snap to uppercase micro-legends that unify the grid.
## HTML
```html
<section class="tfl-16 w-full min-h-svh grid place-items-center bg-tfl16-bg px-5 py-12 font-tfl16-sans" aria-label="Multi-column form grid floating labels demo">
<form class="@container/form flex w-full max-w-[560px] flex-col gap-[22px] rounded-3xl bg-white p-8 shadow-[0_24px_60px_-32px_oklch(0.3_0.06_265/0.4)] ring-1 ring-tfl16-ink/5" novalidate>
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="text-2xl font-bold tracking-[-0.02em] text-tfl16-ink">Shipping address</h3>
<span class="text-xs font-semibold tracking-[.12em] text-tfl16-mut uppercase">Step 1 of 2</span>
</div>
<div class="grid grid-cols-6 gap-4">
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-first" name="given-name" placeholder=" " autocomplete="given-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-first">First name</label>
</div>
<div class="relative col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-last" name="family-name" placeholder=" " autocomplete="family-name">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-last">Last name</label>
</div>
<div class="relative col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-street" name="street-address" placeholder=" " autocomplete="street-address">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-street">Street address</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-city" name="address-level2" placeholder=" " autocomplete="address-level2">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-city">City</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-state" name="address-level1" placeholder=" " autocomplete="address-level1">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-state">State</label>
</div>
<div class="relative col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6">
<input class="peer h-14 w-full rounded-xl border-[1.5px] border-tfl16-line bg-white px-4 pt-[22px] pb-1.5 text-base text-tfl16-ink tabular-nums outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl16-line-hover focus:border-tfl16-acc focus:ring-4 focus:ring-tfl16-acc/15" type="text" id="tfl-16-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 max-w-[calc(100%_-_28px)] origin-left -translate-y-1/2 truncate text-[15px] text-tfl16-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_14px)] peer-focus:scale-70 peer-focus:font-bold peer-focus:tracking-[.12em] peer-focus:text-tfl16-acc peer-focus:uppercase peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_14px)] peer-[:not(:placeholder-shown)]:scale-70 peer-[:not(:placeholder-shown)]:font-bold peer-[:not(:placeholder-shown)]:tracking-[.12em] peer-[:not(:placeholder-shown)]:uppercase motion-reduce:transition-none" for="tfl-16-zip">ZIP</label>
</div>
</div>
<button class="min-h-[52px] cursor-pointer rounded-xl bg-tfl16-acc text-[15px] font-semibold text-white shadow-[0_10px_24px_-12px_var(--color-tfl16-acc)] transition-[filter,scale] duration-150 hover:brightness-115 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl16-acc/50" type="button">Continue to payment</button>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl16-bg: #f0f1f5;
--color-tfl16-bg: oklch(0.96 0.006 270);
--color-tfl16-ink: #191c2b;
--color-tfl16-ink: oklch(0.22 0.03 270);
--color-tfl16-mut: #62677c;
--color-tfl16-mut: oklch(0.5 0.03 270);
--color-tfl16-line: #d6d9e4;
--color-tfl16-line: oklch(0.88 0.015 270);
--color-tfl16-line-hover: #b4b9cc;
--color-tfl16-line-hover: oklch(0.78 0.025 270);
--color-tfl16-acc: #3d4fc4;
--color-tfl16-acc: oklch(0.49 0.18 268);
--font-tfl16-sans: 'IBM Plex Sans', system-ui, sans-serif;
}
.tfl-16 {
width: 100%;
min-height: 100svh;
}
```How this works
The field doesn't know the grid exists. Each wrapper is the same self-contained relative box; only its col-span-* differs, so the float behaves identically at any width.
Two named breakpoints, not one. Below 26rem every field goes full-width; between that and 32rem City takes a full row while State and ZIP pair up:
<div class="col-span-2 @max-[32rem]/form:col-span-6 @max-[26rem]/form:col-span-6"> <!-- City -->
<div class="col-span-2 @max-[32rem]/form:col-span-3 @max-[26rem]/form:col-span-6"> <!-- State -->Make it yours
- Field width: its
col-span-*(out of 6). - Collapse points: the
26rem/32remcontainer thresholds. - Gap: one
gap-4for rows and columns. - Keep labels natural-case: drop
uppercaseandtracking-[.12em].
Gotchas — read before shipping
- Container queries measure the container's content box — padding is excluded, so thresholds must be smaller than the form's outer width.
- Give every field the same height or shared rows rag.
- Keep
autocompleteon every address field; grids are exactly where autofill shines (see demo 05).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Container queries are baseline since 2023 and inside Tailwind v4's floor.