13 / 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
Responsive Tailwind Floating Label for Mobile (No iOS Zoom)
The mobile survival kit, now sized to the form rather than the screen: the form is an @container, and heights, paddings, type and the float distance are clamp()s over cqi container units. Input text is floored at 16px so iOS Safari never zoom-jumps, every target is ≥48px, and a tinted highlight lane gives the floated label its own track.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tfl-13 w-full min-h-svh grid place-items-center bg-tfl13-bg px-[clamp(12px,4vw,24px)] py-[clamp(16px,5vw,48px)] font-tfl13-sans" aria-label="Responsive mobile floating label demo">
<form class="@container w-full max-w-[420px] rounded-[clamp(14px,3vw,22px)] bg-white shadow-[0_22px_55px_-30px_oklch(0.3_0.06_150/0.4)] ring-1 ring-tfl13-ink/5" novalidate>
<div class="flex flex-col gap-[clamp(14px,4.5cqi,22px)] px-[clamp(16px,6cqi,32px)] py-[clamp(20px,7cqi,36px)]">
<div class="grid gap-1">
<h3 class="text-[clamp(21px,6.2cqi,26px)] font-extrabold tracking-[-0.02em] text-tfl13-ink">Get a quote</h3>
<p class="text-[clamp(13px,3.4cqi,14px)]/normal text-tfl13-mut">Resize this panel — type, spacing and float distance scale with the form.</p>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-name">Full name</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="tel" id="tfl-13-tel" name="tel" placeholder=" " inputmode="tel" autocomplete="tel">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-tel">Mobile number</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-zip">ZIP / Postcode</label>
</div>
<button class="min-h-12 h-[clamp(48px,13cqi,54px)] cursor-pointer rounded-xl bg-tfl13-acc-ink text-[clamp(15px,4cqi,16px)] font-bold text-white transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl13-acc/60" type="button">Send my quote</button>
</div>
</form>
</section><section class="tfl-13 w-full min-h-svh grid place-items-center bg-tfl13-bg px-[clamp(12px,4vw,24px)] py-[clamp(16px,5vw,48px)] font-tfl13-sans" aria-label="Responsive mobile floating label demo">
<form class="@container w-full max-w-[420px] rounded-[clamp(14px,3vw,22px)] bg-white shadow-[0_22px_55px_-30px_oklch(0.3_0.06_150/0.4)] ring-1 ring-tfl13-ink/5" novalidate>
<div class="flex flex-col gap-[clamp(14px,4.5cqi,22px)] px-[clamp(16px,6cqi,32px)] py-[clamp(20px,7cqi,36px)]">
<div class="grid gap-1">
<h3 class="text-[clamp(21px,6.2cqi,26px)] font-extrabold tracking-[-0.02em] text-tfl13-ink">Get a quote</h3>
<p class="text-[clamp(13px,3.4cqi,14px)]/normal text-tfl13-mut">Resize this panel — type, spacing and float distance scale with the form.</p>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-name">Full name</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="tel" id="tfl-13-tel" name="tel" placeholder=" " inputmode="tel" autocomplete="tel">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-tel">Mobile number</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-zip">ZIP / Postcode</label>
</div>
<button class="min-h-12 h-[clamp(48px,13cqi,54px)] cursor-pointer rounded-xl bg-tfl13-acc-ink text-[clamp(15px,4cqi,16px)] font-bold text-white transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl13-acc/60" type="button">Send my quote</button>
</div>
</form>
</section>@import "tailwindcss";
@theme {
--color-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
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-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
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=Hanken+Grotesk: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=Hanken+Grotesk:wght@400..800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Every fluid value is an arbitrary
clamp()in the markup; the--gapcustom property becamegap-[clamp(14px,4.5cqi,22px)]on the form body. .field:has(input:not(:placeholder-shown))::beforebecamehas-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12.- UI upgrade from the CSS original: sizes scale with the form via
@container+cqiinstead ofvw, so the field is correct in a sidebar, a modal or a phone — the original grew on wide desktop screens even when the form itself was narrow. - UI upgrade: white-on-green button text went from 3.1:1 (failing) to 5.2:1 with a deeper
acc-inktoken that also colours the floated label; resting labels rose from#6d7f72to 5.8:1.
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: Responsive Tailwind Floating Label for Mobile (No iOS Zoom)
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/responsive-tailwind-floating-label-mobile-no-ios-zoom/
The mobile survival kit, now sized to the form rather than the screen: the form is an @container, and heights, paddings, type and the float distance are clamp()s over cqi container units. Input text is floored at 16px so iOS Safari never zoom-jumps, every target is ≥48px, and a tinted highlight lane gives the floated label its own track.
## HTML
```html
<section class="tfl-13 w-full min-h-svh grid place-items-center bg-tfl13-bg px-[clamp(12px,4vw,24px)] py-[clamp(16px,5vw,48px)] font-tfl13-sans" aria-label="Responsive mobile floating label demo">
<form class="@container w-full max-w-[420px] rounded-[clamp(14px,3vw,22px)] bg-white shadow-[0_22px_55px_-30px_oklch(0.3_0.06_150/0.4)] ring-1 ring-tfl13-ink/5" novalidate>
<div class="flex flex-col gap-[clamp(14px,4.5cqi,22px)] px-[clamp(16px,6cqi,32px)] py-[clamp(20px,7cqi,36px)]">
<div class="grid gap-1">
<h3 class="text-[clamp(21px,6.2cqi,26px)] font-extrabold tracking-[-0.02em] text-tfl13-ink">Get a quote</h3>
<p class="text-[clamp(13px,3.4cqi,14px)]/normal text-tfl13-mut">Resize this panel — type, spacing and float distance scale with the form.</p>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-name">Full name</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="tel" id="tfl-13-tel" name="tel" placeholder=" " inputmode="tel" autocomplete="tel">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-tel">Mobile number</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-zip">ZIP / Postcode</label>
</div>
<button class="min-h-12 h-[clamp(48px,13cqi,54px)] cursor-pointer rounded-xl bg-tfl13-acc-ink text-[clamp(15px,4cqi,16px)] font-bold text-white transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl13-acc/60" type="button">Send my quote</button>
</div>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
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: Responsive Tailwind Floating Label for Mobile (No iOS Zoom)
Source: https://codefronts.com/components/tailwind-css-floating-label-inputs/responsive-tailwind-floating-label-mobile-no-ios-zoom/
The mobile survival kit, now sized to the form rather than the screen: the form is an @container, and heights, paddings, type and the float distance are clamp()s over cqi container units. Input text is floored at 16px so iOS Safari never zoom-jumps, every target is ≥48px, and a tinted highlight lane gives the floated label its own track.
## HTML
```html
<section class="tfl-13 w-full min-h-svh grid place-items-center bg-tfl13-bg px-[clamp(12px,4vw,24px)] py-[clamp(16px,5vw,48px)] font-tfl13-sans" aria-label="Responsive mobile floating label demo">
<form class="@container w-full max-w-[420px] rounded-[clamp(14px,3vw,22px)] bg-white shadow-[0_22px_55px_-30px_oklch(0.3_0.06_150/0.4)] ring-1 ring-tfl13-ink/5" novalidate>
<div class="flex flex-col gap-[clamp(14px,4.5cqi,22px)] px-[clamp(16px,6cqi,32px)] py-[clamp(20px,7cqi,36px)]">
<div class="grid gap-1">
<h3 class="text-[clamp(21px,6.2cqi,26px)] font-extrabold tracking-[-0.02em] text-tfl13-ink">Get a quote</h3>
<p class="text-[clamp(13px,3.4cqi,14px)]/normal text-tfl13-mut">Resize this panel — type, spacing and float distance scale with the form.</p>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-name" name="name" placeholder=" " autocomplete="name">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-name">Full name</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="tel" id="tfl-13-tel" name="tel" placeholder=" " inputmode="tel" autocomplete="tel">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-tel">Mobile number</label>
</div>
<div class="relative before:pointer-events-none before:absolute before:inset-x-0.5 before:top-0.5 before:z-[1] before:h-[clamp(22px,6cqi,26px)] before:rounded-t-[10px] before:bg-tfl13-acc/0 before:transition-colors before:duration-[250ms] before:content-[''] focus-within:before:bg-tfl13-acc/12 has-[input:not(:placeholder-shown)]:before:bg-tfl13-acc/12 motion-reduce:before:transition-none">
<input class="peer h-[clamp(54px,14cqi,60px)] w-full rounded-xl border-[1.5px] border-tfl13-line bg-white px-4 pt-[clamp(20px,5.5cqi,24px)] pb-[clamp(4px,1.5cqi,7px)] text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-ink outline-hidden transition-[border-color,box-shadow] duration-200 hover:border-tfl13-line-hover focus:border-tfl13-acc focus:ring-4 focus:ring-tfl13-acc/15" type="text" id="tfl-13-zip" name="postal-code" placeholder=" " inputmode="numeric" autocomplete="postal-code">
<label class="pointer-events-none absolute left-4 top-1/2 z-[2] origin-left -translate-y-1/2 text-[max(16px,clamp(16px,4cqi,17px))] text-tfl13-mut transition-[translate,scale,color] duration-[180ms] peer-focus:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-focus:scale-70 peer-focus:font-semibold peer-focus:text-tfl13-acc-ink peer-[:not(:placeholder-shown)]:translate-y-[calc(-50%_-_clamp(12px,3.5cqi,14px))] peer-[:not(:placeholder-shown)]:scale-70 motion-reduce:transition-none" for="tfl-13-zip">ZIP / Postcode</label>
</div>
<button class="min-h-12 h-[clamp(48px,13cqi,54px)] cursor-pointer rounded-xl bg-tfl13-acc-ink text-[clamp(15px,4cqi,16px)] font-bold text-white transition-[filter,scale] duration-150 hover:brightness-110 active:scale-98 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tfl13-acc/60" type="button">Send my quote</button>
</div>
</form>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tfl13-bg: #eff4ef;
--color-tfl13-bg: oklch(0.96 0.01 150);
--color-tfl13-ink: #17251a;
--color-tfl13-ink: oklch(0.24 0.03 150);
--color-tfl13-mut: #56695b;
--color-tfl13-mut: oklch(0.49 0.03 150);
--color-tfl13-line: #ccd8cf;
--color-tfl13-line: oklch(0.87 0.02 150);
--color-tfl13-line-hover: #a9bcae;
--color-tfl13-line-hover: oklch(0.77 0.03 150);
--color-tfl13-acc: #2ea055;
--color-tfl13-acc: oklch(0.62 0.15 150);
--color-tfl13-acc-ink: #1d7a3d;
--color-tfl13-acc-ink: oklch(0.5 0.13 150);
--font-tfl13-sans: 'Hanken Grotesk', system-ui, sans-serif;
}
.tfl-13 {
width: 100%;
min-height: 100svh;
}
```How this works
Three mobile failures, fixed structurally. iOS zooms the viewport on any input under 16px; floated labels collide with values on short fields; touch targets shrink below 44px. The port expresses every size as a clamp() so the float distance and the field height always scale together:
<input class="h-[clamp(54px,14cqi,60px)] pt-[clamp(20px,5.5cqi,24px)]
text-[max(16px,clamp(16px,4cqi,17px))] …">Why cqi, not vw. The original scaled with the viewport, so the same form looked cramped in a desktop sidebar and roomy on a phone. cqi is 1% of the container's inline size — the form scales to wherever it is placed.
Make it yours
- Tune each
clamp(min, fluid, max)— min is the 300px form, max the 420px form. - Never lower the 16px floor; scale up from it.
- Lane height:
before:h-[clamp(22px,6cqi,26px)]. - Accent:
--color-tfl13-acc.
Gotchas — read before shipping
- Container units need an ancestor with
@container(container-type:inline-size) — without itcqifalls back to the small viewport. - Test at 320px: floated label and first text line must never overlap.
- Don't shrink buttons to fit; 48px minimum (WCAG 2.5.8).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Container units are Chrome 105 / Safari 16 / Firefox 110; the floor is Tailwind v4's own sheet.