30 CSS Grid Layouts03 / 30
Responsive Media Gallery
An auto-fill photo gallery that handles mixed aspect ratios without a single media query: portrait, landscape and square media all snap to a consistent rhythm because aspect-ratio + object-fit normalise every cell. Captions ride as gradient overlays inside real <figure> elements — the markup search engines and screen readers want.
Published
The code
<section class="cgl-03" aria-label="Responsive media gallery demo">
<div class="cgl-03__wall">
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--1" role="img" aria-label="Dune ridges at dusk"></div><figcaption>Dune ridges, 21:9 crop</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--2" role="img" aria-label="Tide pool"></div><figcaption>Tide pool</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--tall" tabindex="0"><div class="cgl-03__img cgl-03__img--3" role="img" aria-label="Canyon slot, portrait"></div><figcaption>Canyon slot, portrait</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--4" role="img" aria-label="Salt flat"></div><figcaption>Salt flat</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--5" role="img" aria-label="Ember sky"></div><figcaption>Ember sky</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--6" role="img" aria-label="Basalt coast"></div><figcaption>Basalt coast</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--7" role="img" aria-label="Storm shelf, panorama"></div><figcaption>Storm shelf, panorama</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--8" role="img" aria-label="Alpine meadow"></div><figcaption>Alpine meadow</figcaption></figure>
</div>
</section><section class="cgl-03" aria-label="Responsive media gallery demo">
<div class="cgl-03__wall">
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--1" role="img" aria-label="Dune ridges at dusk"></div><figcaption>Dune ridges, 21:9 crop</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--2" role="img" aria-label="Tide pool"></div><figcaption>Tide pool</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--tall" tabindex="0"><div class="cgl-03__img cgl-03__img--3" role="img" aria-label="Canyon slot, portrait"></div><figcaption>Canyon slot, portrait</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--4" role="img" aria-label="Salt flat"></div><figcaption>Salt flat</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--5" role="img" aria-label="Ember sky"></div><figcaption>Ember sky</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--6" role="img" aria-label="Basalt coast"></div><figcaption>Basalt coast</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--7" role="img" aria-label="Storm shelf, panorama"></div><figcaption>Storm shelf, panorama</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--8" role="img" aria-label="Alpine meadow"></div><figcaption>Alpine meadow</figcaption></figure>
</div>
</section>.cgl-03,
.cgl-03 *,
.cgl-03 *::before,
.cgl-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-03 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.16 0.02 300);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.2rem;
}
.cgl-03 .cgl-03__wall {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(min(160px,100%),1fr));
grid-auto-rows: auto;
grid-auto-flow: dense;
gap: 10px;
}
.cgl-03 .cgl-03__cell {
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1;
}
.cgl-03 .cgl-03__cell--wide {
grid-column: span 2;
aspect-ratio: 2.05/1;
}
.cgl-03 .cgl-03__cell--tall {
grid-row: span 2;
aspect-ratio: auto;
}
.cgl-03 .cgl-03__img {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
transition: scale .5s ease;
}
.cgl-03 .cgl-03__cell:hover .cgl-03__img,
.cgl-03 .cgl-03__cell:focus-within .cgl-03__img,
.cgl-03 .cgl-03__cell:focus .cgl-03__img {
scale: 1.06;
}
.cgl-03 .cgl-03__img--1 {
background: linear-gradient(120deg,oklch(0.7 0.15 55),oklch(0.45 0.12 25));
}
.cgl-03 .cgl-03__img--2 {
background: radial-gradient(circle at 30% 30%,oklch(0.75 0.12 200),oklch(0.4 0.1 250));
}
.cgl-03 .cgl-03__img--3 {
background: linear-gradient(180deg,oklch(0.65 0.16 40),oklch(0.3 0.08 20));
}
.cgl-03 .cgl-03__img--4 {
background: linear-gradient(150deg,oklch(0.9 0.05 90),oklch(0.6 0.08 60));
}
.cgl-03 .cgl-03__img--5 {
background: radial-gradient(circle at 70% 20%,oklch(0.72 0.18 30),oklch(0.35 0.1 310));
}
.cgl-03 .cgl-03__img--6 {
background: linear-gradient(200deg,oklch(0.55 0.06 240),oklch(0.25 0.04 260));
}
.cgl-03 .cgl-03__img--7 {
background: linear-gradient(90deg,oklch(0.4 0.08 270),oklch(0.6 0.1 320),oklch(0.35 0.07 250));
}
.cgl-03 .cgl-03__img--8 {
background: linear-gradient(160deg,oklch(0.78 0.14 130),oklch(0.45 0.1 160));
}
.cgl-03 figcaption {
position: absolute;
inset: auto 0 0 0;
padding: 1.6rem .8rem .6rem;
font-size: .75rem;
color: oklch(0.97 0.01 90);
background: linear-gradient(180deg,transparent,oklch(0.1 0.02 300/.85));
opacity: 0;
translate: 0 6px;
transition: opacity .3s ease,translate .3s ease;
}
.cgl-03 .cgl-03__cell:hover figcaption,
.cgl-03 .cgl-03__cell:focus-within figcaption,
.cgl-03 .cgl-03__cell:focus figcaption {
opacity: 1;
translate: 0 0;
}
.cgl-03 .cgl-03__cell:focus-visible {
outline: 2px solid oklch(0.8 0.15 55);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cgl-03 .cgl-03__img,
.cgl-03 figcaption {
transition: none;
}
}.cgl-03,
.cgl-03 *,
.cgl-03 *::before,
.cgl-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-03 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.16 0.02 300);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.2rem;
}
.cgl-03 .cgl-03__wall {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(min(160px,100%),1fr));
grid-auto-rows: auto;
grid-auto-flow: dense;
gap: 10px;
}
.cgl-03 .cgl-03__cell {
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1;
}
.cgl-03 .cgl-03__cell--wide {
grid-column: span 2;
aspect-ratio: 2.05/1;
}
.cgl-03 .cgl-03__cell--tall {
grid-row: span 2;
aspect-ratio: auto;
}
.cgl-03 .cgl-03__img {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
transition: scale .5s ease;
}
.cgl-03 .cgl-03__cell:hover .cgl-03__img,
.cgl-03 .cgl-03__cell:focus-within .cgl-03__img,
.cgl-03 .cgl-03__cell:focus .cgl-03__img {
scale: 1.06;
}
.cgl-03 .cgl-03__img--1 {
background: linear-gradient(120deg,oklch(0.7 0.15 55),oklch(0.45 0.12 25));
}
.cgl-03 .cgl-03__img--2 {
background: radial-gradient(circle at 30% 30%,oklch(0.75 0.12 200),oklch(0.4 0.1 250));
}
.cgl-03 .cgl-03__img--3 {
background: linear-gradient(180deg,oklch(0.65 0.16 40),oklch(0.3 0.08 20));
}
.cgl-03 .cgl-03__img--4 {
background: linear-gradient(150deg,oklch(0.9 0.05 90),oklch(0.6 0.08 60));
}
.cgl-03 .cgl-03__img--5 {
background: radial-gradient(circle at 70% 20%,oklch(0.72 0.18 30),oklch(0.35 0.1 310));
}
.cgl-03 .cgl-03__img--6 {
background: linear-gradient(200deg,oklch(0.55 0.06 240),oklch(0.25 0.04 260));
}
.cgl-03 .cgl-03__img--7 {
background: linear-gradient(90deg,oklch(0.4 0.08 270),oklch(0.6 0.1 320),oklch(0.35 0.07 250));
}
.cgl-03 .cgl-03__img--8 {
background: linear-gradient(160deg,oklch(0.78 0.14 130),oklch(0.45 0.1 160));
}
.cgl-03 figcaption {
position: absolute;
inset: auto 0 0 0;
padding: 1.6rem .8rem .6rem;
font-size: .75rem;
color: oklch(0.97 0.01 90);
background: linear-gradient(180deg,transparent,oklch(0.1 0.02 300/.85));
opacity: 0;
translate: 0 6px;
transition: opacity .3s ease,translate .3s ease;
}
.cgl-03 .cgl-03__cell:hover figcaption,
.cgl-03 .cgl-03__cell:focus-within figcaption,
.cgl-03 .cgl-03__cell:focus figcaption {
opacity: 1;
translate: 0 0;
}
.cgl-03 .cgl-03__cell:focus-visible {
outline: 2px solid oklch(0.8 0.15 55);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cgl-03 .cgl-03__img,
.cgl-03 figcaption {
transition: none;
}
}/* No JavaScript — auto-fill + minmax(min(160px,100%),1fr) sizes the wall; aspect-ratio + object-fit normalise mixed media. */
/* No JavaScript — auto-fill + minmax(min(160px,100%),1fr) sizes the wall; aspect-ratio + object-fit normalise mixed media. */Here's a working CSS Grid Layout 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 Media Gallery
Source: https://codefronts.com/layouts/css-grid-layouts/responsive-media-gallery/
An auto-fill photo gallery that handles mixed aspect ratios without a single media query: portrait, landscape and square media all snap to a consistent rhythm because aspect-ratio + object-fit normalise every cell. Captions ride as gradient overlays inside real <figure> elements — the markup search engines and screen readers want.
## HTML
```html
<section class="cgl-03" aria-label="Responsive media gallery demo">
<div class="cgl-03__wall">
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--1" role="img" aria-label="Dune ridges at dusk"></div><figcaption>Dune ridges, 21:9 crop</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--2" role="img" aria-label="Tide pool"></div><figcaption>Tide pool</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--tall" tabindex="0"><div class="cgl-03__img cgl-03__img--3" role="img" aria-label="Canyon slot, portrait"></div><figcaption>Canyon slot, portrait</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--4" role="img" aria-label="Salt flat"></div><figcaption>Salt flat</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--5" role="img" aria-label="Ember sky"></div><figcaption>Ember sky</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--6" role="img" aria-label="Basalt coast"></div><figcaption>Basalt coast</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--7" role="img" aria-label="Storm shelf, panorama"></div><figcaption>Storm shelf, panorama</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--8" role="img" aria-label="Alpine meadow"></div><figcaption>Alpine meadow</figcaption></figure>
</div>
</section>
```
## CSS
```css
.cgl-03,
.cgl-03 *,
.cgl-03 *::before,
.cgl-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-03 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.16 0.02 300);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.2rem;
}
.cgl-03 .cgl-03__wall {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(min(160px,100%),1fr));
grid-auto-rows: auto;
grid-auto-flow: dense;
gap: 10px;
}
.cgl-03 .cgl-03__cell {
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1;
}
.cgl-03 .cgl-03__cell--wide {
grid-column: span 2;
aspect-ratio: 2.05/1;
}
.cgl-03 .cgl-03__cell--tall {
grid-row: span 2;
aspect-ratio: auto;
}
.cgl-03 .cgl-03__img {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
transition: scale .5s ease;
}
.cgl-03 .cgl-03__cell:hover .cgl-03__img,
.cgl-03 .cgl-03__cell:focus-within .cgl-03__img,
.cgl-03 .cgl-03__cell:focus .cgl-03__img {
scale: 1.06;
}
.cgl-03 .cgl-03__img--1 {
background: linear-gradient(120deg,oklch(0.7 0.15 55),oklch(0.45 0.12 25));
}
.cgl-03 .cgl-03__img--2 {
background: radial-gradient(circle at 30% 30%,oklch(0.75 0.12 200),oklch(0.4 0.1 250));
}
.cgl-03 .cgl-03__img--3 {
background: linear-gradient(180deg,oklch(0.65 0.16 40),oklch(0.3 0.08 20));
}
.cgl-03 .cgl-03__img--4 {
background: linear-gradient(150deg,oklch(0.9 0.05 90),oklch(0.6 0.08 60));
}
.cgl-03 .cgl-03__img--5 {
background: radial-gradient(circle at 70% 20%,oklch(0.72 0.18 30),oklch(0.35 0.1 310));
}
.cgl-03 .cgl-03__img--6 {
background: linear-gradient(200deg,oklch(0.55 0.06 240),oklch(0.25 0.04 260));
}
.cgl-03 .cgl-03__img--7 {
background: linear-gradient(90deg,oklch(0.4 0.08 270),oklch(0.6 0.1 320),oklch(0.35 0.07 250));
}
.cgl-03 .cgl-03__img--8 {
background: linear-gradient(160deg,oklch(0.78 0.14 130),oklch(0.45 0.1 160));
}
.cgl-03 figcaption {
position: absolute;
inset: auto 0 0 0;
padding: 1.6rem .8rem .6rem;
font-size: .75rem;
color: oklch(0.97 0.01 90);
background: linear-gradient(180deg,transparent,oklch(0.1 0.02 300/.85));
opacity: 0;
translate: 0 6px;
transition: opacity .3s ease,translate .3s ease;
}
.cgl-03 .cgl-03__cell:hover figcaption,
.cgl-03 .cgl-03__cell:focus-within figcaption,
.cgl-03 .cgl-03__cell:focus figcaption {
opacity: 1;
translate: 0 0;
}
.cgl-03 .cgl-03__cell:focus-visible {
outline: 2px solid oklch(0.8 0.15 55);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cgl-03 .cgl-03__img,
.cgl-03 figcaption {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — auto-fill + minmax(min(160px,100%),1fr) sizes the wall; aspect-ratio + object-fit normalise mixed media. */
```Here's a working CSS Grid Layout 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 Media Gallery
Source: https://codefronts.com/layouts/css-grid-layouts/responsive-media-gallery/
An auto-fill photo gallery that handles mixed aspect ratios without a single media query: portrait, landscape and square media all snap to a consistent rhythm because aspect-ratio + object-fit normalise every cell. Captions ride as gradient overlays inside real <figure> elements — the markup search engines and screen readers want.
## HTML
```html
<section class="cgl-03" aria-label="Responsive media gallery demo">
<div class="cgl-03__wall">
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--1" role="img" aria-label="Dune ridges at dusk"></div><figcaption>Dune ridges, 21:9 crop</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--2" role="img" aria-label="Tide pool"></div><figcaption>Tide pool</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--tall" tabindex="0"><div class="cgl-03__img cgl-03__img--3" role="img" aria-label="Canyon slot, portrait"></div><figcaption>Canyon slot, portrait</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--4" role="img" aria-label="Salt flat"></div><figcaption>Salt flat</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--5" role="img" aria-label="Ember sky"></div><figcaption>Ember sky</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--6" role="img" aria-label="Basalt coast"></div><figcaption>Basalt coast</figcaption></figure>
<figure class="cgl-03__cell cgl-03__cell--wide" tabindex="0"><div class="cgl-03__img cgl-03__img--7" role="img" aria-label="Storm shelf, panorama"></div><figcaption>Storm shelf, panorama</figcaption></figure>
<figure class="cgl-03__cell" tabindex="0"><div class="cgl-03__img cgl-03__img--8" role="img" aria-label="Alpine meadow"></div><figcaption>Alpine meadow</figcaption></figure>
</div>
</section>
```
## CSS
```css
.cgl-03,
.cgl-03 *,
.cgl-03 *::before,
.cgl-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cgl-03 {
width: 100%;
min-height: 100vh;
--bg: oklch(0.16 0.02 300);
font-family: system-ui,sans-serif;
background: var(--bg);
padding: 1.2rem;
}
.cgl-03 .cgl-03__wall {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(min(160px,100%),1fr));
grid-auto-rows: auto;
grid-auto-flow: dense;
gap: 10px;
}
.cgl-03 .cgl-03__cell {
position: relative;
overflow: hidden;
border-radius: 12px;
aspect-ratio: 1;
}
.cgl-03 .cgl-03__cell--wide {
grid-column: span 2;
aspect-ratio: 2.05/1;
}
.cgl-03 .cgl-03__cell--tall {
grid-row: span 2;
aspect-ratio: auto;
}
.cgl-03 .cgl-03__img {
inline-size: 100%;
block-size: 100%;
object-fit: cover;
transition: scale .5s ease;
}
.cgl-03 .cgl-03__cell:hover .cgl-03__img,
.cgl-03 .cgl-03__cell:focus-within .cgl-03__img,
.cgl-03 .cgl-03__cell:focus .cgl-03__img {
scale: 1.06;
}
.cgl-03 .cgl-03__img--1 {
background: linear-gradient(120deg,oklch(0.7 0.15 55),oklch(0.45 0.12 25));
}
.cgl-03 .cgl-03__img--2 {
background: radial-gradient(circle at 30% 30%,oklch(0.75 0.12 200),oklch(0.4 0.1 250));
}
.cgl-03 .cgl-03__img--3 {
background: linear-gradient(180deg,oklch(0.65 0.16 40),oklch(0.3 0.08 20));
}
.cgl-03 .cgl-03__img--4 {
background: linear-gradient(150deg,oklch(0.9 0.05 90),oklch(0.6 0.08 60));
}
.cgl-03 .cgl-03__img--5 {
background: radial-gradient(circle at 70% 20%,oklch(0.72 0.18 30),oklch(0.35 0.1 310));
}
.cgl-03 .cgl-03__img--6 {
background: linear-gradient(200deg,oklch(0.55 0.06 240),oklch(0.25 0.04 260));
}
.cgl-03 .cgl-03__img--7 {
background: linear-gradient(90deg,oklch(0.4 0.08 270),oklch(0.6 0.1 320),oklch(0.35 0.07 250));
}
.cgl-03 .cgl-03__img--8 {
background: linear-gradient(160deg,oklch(0.78 0.14 130),oklch(0.45 0.1 160));
}
.cgl-03 figcaption {
position: absolute;
inset: auto 0 0 0;
padding: 1.6rem .8rem .6rem;
font-size: .75rem;
color: oklch(0.97 0.01 90);
background: linear-gradient(180deg,transparent,oklch(0.1 0.02 300/.85));
opacity: 0;
translate: 0 6px;
transition: opacity .3s ease,translate .3s ease;
}
.cgl-03 .cgl-03__cell:hover figcaption,
.cgl-03 .cgl-03__cell:focus-within figcaption,
.cgl-03 .cgl-03__cell:focus figcaption {
opacity: 1;
translate: 0 0;
}
.cgl-03 .cgl-03__cell:focus-visible {
outline: 2px solid oklch(0.8 0.15 55);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cgl-03 .cgl-03__img,
.cgl-03 figcaption {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — auto-fill + minmax(min(160px,100%),1fr) sizes the wall; aspect-ratio + object-fit normalise mixed media. */
```How this works
The wall is grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr)). auto-fill keeps ghost tracks so the last row doesn't over-stretch, and the inner min() stops the 160px floor from overflowing viewports narrower than one column — the crash-proof version of the classic pattern.
Every cell is a <figure> with aspect-ratio: 1; feature tiles span 2 tracks and switch to aspect-ratio: 2/1 plus grid-row: span 2 variants for portraits. Whatever the source media's ratio, object-fit: cover crops it to the cell — zero layout shift, zero stretched pixels. Captions are absolutely-positioned <figcaption>s over a bottom gradient scrim, revealed on hover/focus-within.
Make it yours
- Raise the 160px floor for larger thumbnails — the column count adapts automatically.
- Switch
auto-filltoauto-fitif you want few items to stretch wide instead of leaving ghost tracks. - Change the default cell ratio to 4/5 for a portrait-first (Instagram-style) wall.
- Make captions always visible by removing the opacity toggle — better for accessibility-first galleries.
Gotchas — read before shipping
- Without the inner
min(), minmax(160px,1fr) overflows any viewport under ~192px — the #1 bug report on this pattern. - Spanning tiles must keep their aspect-ratio consistent with the span (2 tracks ≈ 2/1) or rows ripple unevenly.
- Hover-only captions fail keyboard users — the
:focus-withinreveal (included) is not optional.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 88+.
aspect-ratio is the gate (2021 evergreens). The grid math itself works back to 2017; oklch gradient placeholders swap for real images in production.