09 / 15
All 15 in 15 Tailwind Card Grid Layouts
- 01Tailwind Stats Dashboard KPI Grid
- 02Tailwind Hero Photo Collage Grid
- 03Tailwind Pricing Comparison Card Grid
- 04Tailwind Testimonial Wall Grid
- 05Tailwind Editorial Tabloid Bento Layout
- 06Tailwind Featured Article Magazine Grid
- 07Tailwind Asymmetric Bento Box Feature Grid
- 08Tailwind Stratified Bento Grid Layout
- 09Tailwind Equal-Height Product Grid
- 10Tailwind Fluid Masonry Card Grid
- 11Tailwind Auto-Fit Blog Archive Grid
- 12Tailwind Team Directory Face Grid
- 13Tailwind 3D Hover Flip Card Grid
- 14Tailwind Use-Case Showcase Card Grid
- 15Tailwind Glass Bento Grid Layout
Tailwind Equal-Height Product Grid
The alignment problem every catalogue has — titles of different lengths pushing the Add-to-Cart buttons out of line — solved in four utilities: items-stretch on the grid, h-full flex flex-col on the card, and mt-auto on the price row. The port's value here is that the contract is now visible on the elements that make it.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcg-03 w-full min-h-svh bg-tcg03-bg font-tcg03-sans text-tcg03-ink px-6 py-12" aria-label="Equal-height product card grid demo">
<div class="max-w-[980px] mx-auto grid [grid-template-columns:repeat(auto-fill,minmax(220px,1fr))] items-stretch gap-6">
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#fde68a,#f59e0b]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Ceramic Pour-Over Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Hand-glazed dripper and carafe.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$48</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#a7f3d0,#10b981]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Walnut Cutting Board With Reversible Edge and Juice Groove</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">FSC-certified solid walnut, oiled and ready for daily use in any kitchen.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$72</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#bfdbfe,#3b82f6]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Linen Napkin Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Set of four, stonewashed for softness.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$34</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</div>
</section><section class="tcg-03 w-full min-h-svh bg-tcg03-bg font-tcg03-sans text-tcg03-ink px-6 py-12" aria-label="Equal-height product card grid demo">
<div class="max-w-[980px] mx-auto grid [grid-template-columns:repeat(auto-fill,minmax(220px,1fr))] items-stretch gap-6">
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#fde68a,#f59e0b]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Ceramic Pour-Over Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Hand-glazed dripper and carafe.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$48</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#a7f3d0,#10b981]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Walnut Cutting Board With Reversible Edge and Juice Groove</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">FSC-certified solid walnut, oiled and ready for daily use in any kitchen.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$72</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#bfdbfe,#3b82f6]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Linen Napkin Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Set of four, stonewashed for softness.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$34</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</div>
</section>@import "tailwindcss";
@theme {
--color-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}@import "tailwindcss";
@theme {
--color-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}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-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}@import "tailwindcss";
@theme {
--color-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}<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">
<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.
- This is the smallest port in the collection and the clearest: four utilities on three elements now state the entire equal-height contract, where the CSS version stated it across four rules a designer had to assemble mentally.
auto-fillis preserved deliberately. It is one word different from demo 01'sauto-fitand it is the difference between keeping empty tracks and collapsing them.- The card gained
focus-within:-translate-y-[3px]so tabbing to the Add-to-Cart button lifts the card the same way hovering does — a keyboard user now gets the same feedback. - The button went from 34px tall to
min-h-11. The visual change is small; the compliance change is the point. line-clamp-2is offered as a customization rather than shipped, because clamping is a content decision and the auto-margin already solves the alignment.- The reduced-motion block becomes
motion-reduce:transition-none motion-reduce:hover:translate-y-0on the card and the button, which is two utilities each instead of a selector list at the bottom of the sheet.
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 Equal-Height Product Grid
Source: https://codefronts.com/layouts/tailwind-card-grid-layout/tailwind-equal-height-product-grid/
The alignment problem every catalogue has — titles of different lengths pushing the Add-to-Cart buttons out of line — solved in four utilities: items-stretch on the grid, h-full flex flex-col on the card, and mt-auto on the price row. The port's value here is that the contract is now visible on the elements that make it.
## HTML
```html
<section class="tcg-03 w-full min-h-svh bg-tcg03-bg font-tcg03-sans text-tcg03-ink px-6 py-12" aria-label="Equal-height product card grid demo">
<div class="max-w-[980px] mx-auto grid [grid-template-columns:repeat(auto-fill,minmax(220px,1fr))] items-stretch gap-6">
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#fde68a,#f59e0b]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Ceramic Pour-Over Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Hand-glazed dripper and carafe.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$48</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#a7f3d0,#10b981]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Walnut Cutting Board With Reversible Edge and Juice Groove</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">FSC-certified solid walnut, oiled and ready for daily use in any kitchen.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$72</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#bfdbfe,#3b82f6]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Linen Napkin Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Set of four, stonewashed for softness.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$34</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}
```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 Equal-Height Product Grid
Source: https://codefronts.com/layouts/tailwind-card-grid-layout/tailwind-equal-height-product-grid/
The alignment problem every catalogue has — titles of different lengths pushing the Add-to-Cart buttons out of line — solved in four utilities: items-stretch on the grid, h-full flex flex-col on the card, and mt-auto on the price row. The port's value here is that the contract is now visible on the elements that make it.
## HTML
```html
<section class="tcg-03 w-full min-h-svh bg-tcg03-bg font-tcg03-sans text-tcg03-ink px-6 py-12" aria-label="Equal-height product card grid demo">
<div class="max-w-[980px] mx-auto grid [grid-template-columns:repeat(auto-fill,minmax(220px,1fr))] items-stretch gap-6">
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#fde68a,#f59e0b]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Ceramic Pour-Over Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Hand-glazed dripper and carafe.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$48</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#a7f3d0,#10b981]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Walnut Cutting Board With Reversible Edge and Juice Groove</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">FSC-certified solid walnut, oiled and ready for daily use in any kitchen.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$72</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
<article class="h-full flex flex-col p-5 rounded-[14px] border border-tcg03-line bg-tcg03-card transition-[translate,box-shadow] duration-250 hover:-translate-y-[3px] hover:shadow-[0_14px_30px_-18px_oklch(0.3_0.02_260/0.25)] focus-within:-translate-y-[3px] motion-reduce:transition-none motion-reduce:hover:translate-y-0">
<div class="w-full aspect-[4/3] mb-3.5 rounded-[10px] bg-linear-[135deg,#bfdbfe,#3b82f6]" aria-hidden="true"></div>
<h3 class="text-[.98rem] font-bold leading-[1.35] mb-1.5">Linen Napkin Set</h3>
<p class="text-[.82rem] leading-[1.5] text-tcg03-muted text-pretty">Set of four, stonewashed for softness.</p>
<div class="mt-auto pt-4 flex items-center justify-between gap-2.5">
<span class="text-[1.05rem] font-extrabold tabular-nums">$34</span>
<button type="button" class="min-h-11 px-3.5 rounded-lg border-0 bg-tcg03-ink text-white font-tcg03-sans text-[.78rem] font-bold cursor-pointer transition-[background] duration-200 hover:bg-tcg03-inkdeep focus-visible:outline-2 focus-visible:outline-tcg03-accent focus-visible:outline-offset-2 motion-reduce:transition-none">Add to Cart</button>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcg03-bg: #f8f7f4;
--color-tcg03-bg: oklch(0.97 0.005 90);
--color-tcg03-card: #ffffff;
--color-tcg03-card: oklch(1 0 0);
--color-tcg03-ink: #1f2937;
--color-tcg03-ink: oklch(0.29 0.03 260);
--color-tcg03-inkdeep: #111827;
--color-tcg03-inkdeep: oklch(0.21 0.03 265);
--color-tcg03-muted: #6b7280;
--color-tcg03-muted: oklch(0.55 0.02 260);
--color-tcg03-line: #e7e4dd;
--color-tcg03-line: oklch(0.91 0.008 90);
--color-tcg03-accent: #f59e0b;
--color-tcg03-accent: oklch(0.77 0.16 70);
--font-tcg03-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Root rule: the full-bleed contract. */
.tcg-03 {
width: 100%;
min-height: 100svh;
}
.tcg-03 ::selection {
background: #f59e0b;
color: #1a1a1a;
}
```How this works
Grid stretches; flex distributes. The track list gives equal columns and grid's default align-items: stretch makes every cell as tall as the tallest in its row. The card then has to actually fill that cell:
<article class="h-full flex flex-col …">
…
<div class="mt-auto pt-4 flex items-center justify-between">h-full is the utility people forget; without it the card hugs its content and the whole trick quietly fails. mt-auto then pushes the price row to the bottom regardless of whether the title wrapped once or three times.
The image is part of the alignment. aspect-[4/3] guarantees every card's media block is the same height, so the text starts on a shared line too — bottom alignment alone still looks ragged if the images differ.
auto-fill, not auto-fit. The original chose auto-fill so a short catalogue keeps its column rhythm instead of stretching three cards across a 1400px row; the port keeps that choice, and it is the one-word difference worth knowing between this demo and demo 01.
Make it yours
- Add
line-clamp-2to the title for strict two-line parity before the auto-margin does its work. - Swap the CTA for an icon button: replace the button's padding with
w-10 h-10 rounded-full;mt-autoon the row is unaffected. - Densify or loosen the whole catalogue by editing the
220pxfloor in the arbitrary track list. - Add a sale price by wrapping the old figure in
<s class="opacity-50">inside the price cell. - Retheme through
--color-tcg03-bg,--color-tcg03-inkand--color-tcg03-accentin@theme.
Gotchas — read before shipping
- Without
h-fullon the card the alignment silently breaks: grid stretches the cell, the card does not fill it, andmt-autohas no leftover space to absorb. - An intrinsic
<img>with noaspect-*reintroduces misalignment above the text even though the CTA row still lines up. items-stretchis grid's default, but an inheriteditems-startfrom a parent layout will override it — state it explicitly, as this demo does.auto-fillkeeps empty tracks; on an ultra-wide screen with three products you get three cards and empty columns. That is the intended behaviour here — useauto-fitif you want them to stretch.- The Add-to-Cart button needs
min-h-11: a 34px-tall button is under the 44px touch-target minimum, which is what the CSS version shipped.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Grid auto-stretch plus flexbox auto-margins are baseline everywhere; aspect-ratio needs Safari 15+. The floor quoted is Tailwind v4's own generated sheet.