Pure TailwindMIT licensed

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.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

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>
@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.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@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;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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 plain col-span-* utilities; the named @container flb16form became @container/form with @max-[…]/form: variants.
  • The ellipsis guard (overflow/text-overflow/white-space) became one truncate.
  • 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.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 / 32rem container thresholds.
  • Gap: one gap-4 for rows and columns.
  • Keep labels natural-case: drop uppercase and tracking-[.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 autocomplete on every address field; grids are exactly where autofill shines (see demo 05).

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Container queries are baseline since 2023 and inside Tailwind v4's floor.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…