30 Tailwind CSS Grid Layouts
03 / 30

All 30 in 30 Tailwind CSS Grid Layouts

  1. 01Tailwind Full-Bleed Article Layout for Long-Form Editorial
  2. 02Tailwind Docs Layout with Sticky Table of Contents
  3. 03Tailwind Photo Gallery Grid with auto-fill and Real Images
  4. 04Tailwind Holy Grail Page Layout for a News Site
  5. 05Tailwind Data Table with Subgrid Rows and Status Pills
  6. 06Tailwind Overlapping Card Hero for a Case Study
  7. 07Tailwind Help Center Layout with Fixed Sidebars
  8. 08Tailwind 12 Column Grid System for Marketing Pages
  9. 09Tailwind Bento Grid for a Product Feature Section
  10. 10Tailwind Magazine Spread Layout with Overlapping Headline
  11. 11Tailwind Responsive Card Grid for an Integrations Directory
  12. 12Tailwind CMS Category Grid that Compacts Itself with has()
  13. 13Tailwind Masonry Layout for a Saved Ideas Board
  14. 14Tailwind Hexagon Team Grid with clip-path Avatars
  15. 15Tailwind Subgrid Cards with Perfectly Aligned Buttons
  16. 16Tailwind Analytics Dashboard Layout with Grid App Shell
  17. 17Tailwind Pricing Table with Subgrid and a Featured Plan
  18. 18Tailwind Kanban Board with Scroll Snap Columns
  19. 19Tailwind Video Thumbnail Grid with Zero Layout Shift
  20. 20Tailwind Container Query Card for Reusable Article Teasers
  21. 21Tailwind Diagonal Section Band with skewY Counter-Skew
  22. 22Tailwind Asymmetric Poster Hero with Layered Grid Regions
  23. 23Tailwind Mosaic Photo Wall with a Designed Span Sequence
  24. 24Tailwind Dense Grid Packing for a Mixed-Size Asset Library
  25. 25Tailwind Onboarding Stepper with CSS Counters
  26. 26Tailwind macOS Window Layout with Vibrancy Sidebar
  27. 27Tailwind Scroll Snap Carousel with Peeking Slides
  28. 28Tailwind Status Map UI with Grid Coordinate Pins
  29. 29Tailwind Logo Wall with 1px Gap Hairline Dividers
  30. 30Tailwind Periodic Table Layout with Grid Coordinates
Pure TailwindMIT licensed

Tailwind Photo Gallery Grid with auto-fill and Real Images

A photographer's portfolio index: mixed crops, two feature tiles, captions that surface on hover and on keyboard focus alike. Real images, real lazy loading, and not one media query.

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="tgx-03 w-full min-h-svh bg-tgx03-bg font-tgx03-sans p-6">
  <header class="flex items-end justify-between gap-4 flex-wrap mb-5">
    <div class="flex flex-col gap-1">
      <p class="text-[0.68rem] tracking-[0.2em] uppercase text-tgx03-dim">Marit Solheim · Selected work</p>
      <h2 class="text-[1.4rem] font-semibold text-tgx03-ink tracking-[-0.01em]">North Atlantic, 2024–2026</h2>
    </div>
    <p class="text-[0.78rem] text-tgx03-dim tabular-nums">8 of 64 frames</p>
  </header>
  <div class="grid grid-cols-[repeat(auto-fill,minmax(min(170px,100%),1fr))] [grid-auto-flow:dense] gap-2.5">
    <figure tabindex="0" class="group relative col-span-2 aspect-[2.05/1] overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=900&h=440&q=80&auto=format&fit=crop" alt="Still lake below a wooded ridge at dusk" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Still water, Sørvágur <span class="text-white/70">· 24mm, f/8</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=500&h=500&q=80&auto=format&fit=crop" alt="Sunlight through a dense stand of birch trees" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Birch stand <span class="text-white/70">· 85mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative row-span-2 overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?w=500&h=1000&q=80&auto=format&fit=crop" alt="Tall rock face rising into mist, vertical crop" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Rock face, vertical <span class="text-white/70">· 35mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=500&h=500&q=80&auto=format&fit=crop" alt="Green valley with a river running through it" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">River valley <span class="text-white/70">· 50mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1493246507139-91e8fad9978e?w=500&h=500&q=80&auto=format&fit=crop" alt="Calm sea meeting a pale sky at the horizon" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Horizon study <span class="text-white/70">· 200mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=500&h=500&q=80&auto=format&fit=crop" alt="Sunlit mountain range with low cloud in the valley" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Cloud line <span class="text-white/70">· 24mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative col-span-2 aspect-[2.05/1] overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=900&h=440&q=80&auto=format&fit=crop" alt="Coastal ridge under breaking cloud, panoramic crop" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Ridge, panorama <span class="text-white/70">· stitched, 3 frames</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=500&h=500&q=80&auto=format&fit=crop" alt="Forest path receding into soft light" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Path, late light <span class="text-white/70">· 35mm</span></figcaption>
    </figure>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tgx03-bg: #16161a;
  --color-tgx03-bg: oklch(0.19 0.005 285);
  --color-tgx03-cell: #232329;
  --color-tgx03-cell: oklch(0.26 0.008 285);
  --color-tgx03-ink: #f0efee;
  --color-tgx03-ink: oklch(0.95 0.004 90);
  --color-tgx03-dim: #a3a1a8;
  --color-tgx03-dim: oklch(0.72 0.008 285);
  --color-tgx03-ring: #e6b872;
  --color-tgx03-ring: oklch(0.82 0.11 75);
  --font-tgx03-sans: 'Inter Tight', system-ui, sans-serif;
}

.tgx-03 {
  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-tgx03-bg: #16161a;
  --color-tgx03-bg: oklch(0.19 0.005 285);
  --color-tgx03-cell: #232329;
  --color-tgx03-cell: oklch(0.26 0.008 285);
  --color-tgx03-ink: #f0efee;
  --color-tgx03-ink: oklch(0.95 0.004 90);
  --color-tgx03-dim: #a3a1a8;
  --color-tgx03-dim: oklch(0.72 0.008 285);
  --color-tgx03-ring: #e6b872;
  --color-tgx03-ring: oklch(0.82 0.11 75);
  --font-tgx03-sans: 'Inter Tight', system-ui, sans-serif;
}

.tgx-03 {
  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="preconnect" href="https://images.unsplash.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600&display=swap">

What changed in the port

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

  • Real photography changes what the demo teaches: with gradients you cannot see that object-cover is doing the work. With mixed real crops, the normalisation is the visible result.
  • Each caption carries a lens note in dimmed white — a small pattern worth stealing for any gallery, because it gives the overlay a reason to exist beyond a repeated title.
  • Product upgrade: the wall is now a named photographer's index with a header, a frame count and per-frame metadata; the placeholder gradients became eight real Unsplash images sized to their slots.
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 Photo Gallery Grid with auto-fill and Real Images
Source: https://codefronts.com/layouts/tailwind-css-grid-layouts/tailwind-photo-gallery-grid-auto-fill-real-images/

A photographer's portfolio index: mixed crops, two feature tiles, captions that surface on hover and on keyboard focus alike. Real images, real lazy loading, and not one media query.
## HTML
```html
<section class="tgx-03 w-full min-h-svh bg-tgx03-bg font-tgx03-sans p-6">
  <header class="flex items-end justify-between gap-4 flex-wrap mb-5">
    <div class="flex flex-col gap-1">
      <p class="text-[0.68rem] tracking-[0.2em] uppercase text-tgx03-dim">Marit Solheim · Selected work</p>
      <h2 class="text-[1.4rem] font-semibold text-tgx03-ink tracking-[-0.01em]">North Atlantic, 2024–2026</h2>
    </div>
    <p class="text-[0.78rem] text-tgx03-dim tabular-nums">8 of 64 frames</p>
  </header>
  <div class="grid grid-cols-[repeat(auto-fill,minmax(min(170px,100%),1fr))] [grid-auto-flow:dense] gap-2.5">
    <figure tabindex="0" class="group relative col-span-2 aspect-[2.05/1] overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=900&h=440&q=80&auto=format&fit=crop" alt="Still lake below a wooded ridge at dusk" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Still water, Sørvágur <span class="text-white/70">· 24mm, f/8</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=500&h=500&q=80&auto=format&fit=crop" alt="Sunlight through a dense stand of birch trees" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Birch stand <span class="text-white/70">· 85mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative row-span-2 overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?w=500&h=1000&q=80&auto=format&fit=crop" alt="Tall rock face rising into mist, vertical crop" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Rock face, vertical <span class="text-white/70">· 35mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=500&h=500&q=80&auto=format&fit=crop" alt="Green valley with a river running through it" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">River valley <span class="text-white/70">· 50mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1493246507139-91e8fad9978e?w=500&h=500&q=80&auto=format&fit=crop" alt="Calm sea meeting a pale sky at the horizon" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Horizon study <span class="text-white/70">· 200mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=500&h=500&q=80&auto=format&fit=crop" alt="Sunlit mountain range with low cloud in the valley" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Cloud line <span class="text-white/70">· 24mm</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative col-span-2 aspect-[2.05/1] overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=900&h=440&q=80&auto=format&fit=crop" alt="Coastal ridge under breaking cloud, panoramic crop" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Ridge, panorama <span class="text-white/70">· stitched, 3 frames</span></figcaption>
    </figure>
    <figure tabindex="0" class="group relative aspect-square overflow-hidden rounded-xl bg-tgx03-cell focus-visible:outline-2 focus-visible:outline-tgx03-ring focus-visible:outline-offset-[3px]">
      <img src="https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=500&h=500&q=80&auto=format&fit=crop" alt="Forest path receding into soft light" loading="lazy" class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 group-focus-within:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100">
      <figcaption class="absolute inset-x-0 bottom-0 pt-8 px-3 pb-2.5 text-[0.75rem] text-white bg-[linear-gradient(180deg,transparent,oklch(0.15_0.01_260/0.88))] opacity-0 translate-y-1.5 transition duration-300 group-hover:opacity-100 group-hover:translate-y-0 group-focus-within:opacity-100 group-focus-within:translate-y-0 motion-reduce:transition-none">Path, late light <span class="text-white/70">· 35mm</span></figcaption>
    </figure>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tgx03-bg: #16161a;
  --color-tgx03-bg: oklch(0.19 0.005 285);
  --color-tgx03-cell: #232329;
  --color-tgx03-cell: oklch(0.26 0.008 285);
  --color-tgx03-ink: #f0efee;
  --color-tgx03-ink: oklch(0.95 0.004 90);
  --color-tgx03-dim: #a3a1a8;
  --color-tgx03-dim: oklch(0.72 0.008 285);
  --color-tgx03-ring: #e6b872;
  --color-tgx03-ring: oklch(0.82 0.11 75);
  --font-tgx03-sans: 'Inter Tight', system-ui, sans-serif;
}

.tgx-03 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

auto-fill keeps the rhythm. Ghost tracks stay in the list, so a short last row does not stretch three frames across the wall. min(170px,100%) stops the floor from overflowing a narrow container.

The cell crops, not the photo. Each wrapper owns an aspect ratio and the <img> fills it with object-cover — portrait, landscape and square originals all land on the same rhythm.

Make it yours

  • Raise the 170px floor for bigger thumbnails; the column count follows.
  • Portrait-first wall: swap the base aspect-square for aspect-[4/5].
  • Always-on captions: delete opacity-0 and the two group variants.

Gotchas — read before shipping

  • Hover-only captions fail keyboard users — the group-focus-within: twin is why each figure is focusable.
  • A spanning tile whose ratio does not match its span makes its row taller than the others.
  • Put group on the figure, never the wall: one shared group lights every caption at once.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

aspect-ratio and :focus-within are Baseline 2021.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…