Pure TailwindMIT licensed

Tailwind Fluid Masonry Card Grid

A Pinterest-style wall built on multi-column flow, which is still the only masonry that works everywhere. Tailwind has first-class utilities for the whole technique — columns-3, break-inside-avoid, gap-4 — so this is one of the few demos where the port is shorter than the CSS and nothing has to stay in the stylesheet.

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="tcg-04 w-full min-h-svh bg-tcg04-bg font-tcg04-sans px-6 py-12" aria-label="Fluid masonry card grid demo">
  <div class="max-w-[900px] mx-auto columns-3 max-[640px]:columns-2 gap-4">

    <figure style="--h:280px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fbcfe8,#a855f7] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Portrait Study</figcaption>
    </figure>

    <figure style="--h:180px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#bae6fd,#0284c7] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Harbor Light</figcaption>
    </figure>

    <figure style="--h:340px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fde68a,#d97706] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Desert Bloom</figcaption>
    </figure>

    <figure style="--h:220px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#bbf7d0,#15803d] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Quiet Forest</figcaption>
    </figure>

    <figure style="--h:300px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fecaca,#b91c1c] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Red Canyon</figcaption>
    </figure>

    <figure style="--h:200px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#ddd6fe,#6d28d9] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Twilight Field</figcaption>
    </figure>

  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tcg04-bg: #111114;
  --color-tcg04-bg: oklch(0.17 0.004 285);
  --color-tcg04-card: #1a1a1f;
  --color-tcg04-card: oklch(0.22 0.006 285);
  --font-tcg04-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */

.tcg-04 {
  width: 100%;
  min-height: 100svh;
}

.tcg-04 ::selection {
  background: #a855f7;
  color: #fff;
}

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-tcg04-bg: #111114;
  --color-tcg04-bg: oklch(0.17 0.004 285);
  --color-tcg04-card: #1a1a1f;
  --color-tcg04-card: oklch(0.22 0.006 285);
  --font-tcg04-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */

.tcg-04 {
  width: 100%;
  min-height: 100svh;
}

.tcg-04 ::selection {
  background: #a855f7;
  color: #fff;
}
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=Space+Grotesk:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • Tailwind ships utilities for the entire multicol technique, so this stylesheet holds nothing but tokens, the root rule and a selection colour. It is the shortest CSS field in the collection.
  • break-inside-avoid is a real utility, not an arbitrary property, which is worth noting: the one rule that makes multicol masonry work is a first-class citizen.
  • The per-card --h is a placeholder for real image intrinsic height and disappears entirely once you drop in <img> elements — the demo is honest about being a stand-in.
  • The image zoom moved from a descendant hover rule to group-hover:, so the card and the thing that reacts to hovering it are one class list apart.
  • Captions are legible at rest (opacity-85) rather than hover-only, because a caption that only exists on hover does not exist on a touch device.
  • The single media query becomes max-[640px]:columns-2 — one utility, on the container it governs.
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 Fluid Masonry Card Grid
Source: https://codefronts.com/layouts/tailwind-card-grid-layout/tailwind-fluid-masonry-card-grid/

A Pinterest-style wall built on multi-column flow, which is still the only masonry that works everywhere. Tailwind has first-class utilities for the whole technique &mdash; columns-3, break-inside-avoid, gap-4 &mdash; so this is one of the few demos where the port is shorter than the CSS and nothing has to stay in the stylesheet.
## HTML
```html
<section class="tcg-04 w-full min-h-svh bg-tcg04-bg font-tcg04-sans px-6 py-12" aria-label="Fluid masonry card grid demo">
  <div class="max-w-[900px] mx-auto columns-3 max-[640px]:columns-2 gap-4">

    <figure style="--h:280px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fbcfe8,#a855f7] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Portrait Study</figcaption>
    </figure>

    <figure style="--h:180px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#bae6fd,#0284c7] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Harbor Light</figcaption>
    </figure>

    <figure style="--h:340px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fde68a,#d97706] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Desert Bloom</figcaption>
    </figure>

    <figure style="--h:220px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#bbf7d0,#15803d] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Quiet Forest</figcaption>
    </figure>

    <figure style="--h:300px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#fecaca,#b91c1c] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Red Canyon</figcaption>
    </figure>

    <figure style="--h:200px" class="group relative inline-block w-full mb-4 break-inside-avoid rounded-xl overflow-hidden bg-tcg04-card">
      <div class="w-full h-[var(--h)] bg-linear-[160deg,#ddd6fe,#6d28d9] transition-transform duration-400 group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
      <figcaption class="absolute inset-x-0 bottom-0 px-3 py-2.5 text-[.78rem] font-semibold text-white bg-linear-[0deg,oklch(0_0_0/0.6),transparent] opacity-85 transition-opacity duration-250 group-hover:opacity-100 motion-reduce:transition-none">Twilight Field</figcaption>
    </figure>

  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tcg04-bg: #111114;
  --color-tcg04-bg: oklch(0.17 0.004 285);
  --color-tcg04-card: #1a1a1f;
  --color-tcg04-card: oklch(0.22 0.006 285);
  --font-tcg04-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */

.tcg-04 {
  width: 100%;
  min-height: 100svh;
}

.tcg-04 ::selection {
  background: #a855f7;
  color: #fff;
}
```

How this works

Multicol, not grid. columns-3 plus gap-4 gives three balanced columns; each card is inline-block w-full mb-4 break-inside-avoid so it fills its column's width and never splits. Because columns fill independently, a tall card and a short card sit flush with no leftover gap — the masonry look, from two utilities.

The gutter is two numbers that must agree. gap-4 sets the column gap and mb-4 sets the vertical rhythm. Tailwind's shared spacing scale makes that agreement legible; in CSS they were a column-gap and a margin-bottom in different rules.

Heights stay inline. Each card's --h stands in for a real photo's intrinsic height. It is per-instance data, so it belongs in style and is read back with h-[var(--h)] — with real <img> elements you delete both and let the images size themselves.

Hover zoom without layout cost. overflow-hidden on the card and group-hover:scale-105 on the inner div; the transform runs on the compositor and the column balance never recalculates.

Make it yours

  • Swap columns-3 for columns-[240px] and the browser picks the column count for you — responsive with no breakpoint at all.
  • Hide captions until hover with opacity-0 group-hover:opacity-100 group-focus-within:opacity-100.
  • Change the gutters by editing gap-* and the cards' mb-* together; keeping them equal is what makes the wall read as a grid.
  • Replace the placeholder divs with <img class=&quot;w-full h-auto&quot;> and delete the inline --h.
  • Add a slight tilt on hover with group-hover:rotate-1 for a scrapbook feel.

Gotchas — read before shipping

  • Multicol orders items top-to-bottom per column, not left-to-right. Visual order and DOM order diverge, so keep the source order meaningful for screen readers and keyboard tabbing.
  • Dropping break-inside-avoid lets a card split across a column boundary, which looks like a rendering bug rather than a layout choice.
  • columns-* and CSS Grid do not compose: you cannot span a multicol child across columns the way a grid item spans tracks. If you need a featured tile, use grid and accept ragged bottoms.
  • Native grid-template-rows: masonry is still not interoperable; multicol is the shippable answer in 2026 and this demo is deliberately built on it.
  • Per-card heights must be inline custom properties, not generated class names — the compiler only sees static class strings.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

CSS multi-column and break-inside have universal support and run far further back than the floor quoted, which is Tailwind v4's own generated sheet.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…