12 CSS Masonry Layouts03 / 12
Responsive CSS Masonry Photo Grid Variable Height
The masonry technique nobody blogs about and every feed needs: a normal CSS Grid with tiny 10px auto-rows, where each photo spans a quantized number of rows by height class. Unlike multi-column, items flow LEFT-TO-RIGHT — newest first stays newest first for eyes, screen readers and Tab alike — and unlike JavaScript masonry, a resize costs zero script. Cover-cropping absorbs the fractional differences, so the wall stays gapless at every container width.
Published
The code
<section class="msn-03" aria-label="Responsive variable height masonry photo grid demo">
<div class="msn-03__stage" tabindex="0">
<header class="msn-03__head">
<div><h2>Latest uploads</h2><p>Reading order preserved — newest first</p></div>
<a class="msn-03__cta" href="#submit">Submit a photo</a>
</header>
<div class="msn-03__grid">
<a class="msn-03__card msn-03__card--tall" href="#p1" style="--img:url('https://images.unsplash.com/photo-1469474968028-56623f02e42e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.75 0.1 130);--b:oklch(0.45 0.11 150)"><div class="msn-03__meta"><strong>Valley light</strong><span>Anya R. · 4.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p2" style="--img:url('https://images.unsplash.com/photo-1501594907352-04cda38ebc29?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 40);--b:oklch(0.45 0.13 20)"><div class="msn-03__meta"><strong>Gate in fog</strong><span>Marc D. · 3.1k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p3" style="--img:url('https://images.unsplash.com/photo-1482938289607-e9573fc25ebb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.09 230);--b:oklch(0.45 0.1 250)"><div class="msn-03__meta"><strong>Canyon bend</strong><span>Jules K. · 2.8k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p4" style="--img:url('https://images.unsplash.com/photo-1433086966358-54859d0ed716?q=80&w=800&auto=format&fit=crop');--a:oklch(0.78 0.08 160);--b:oklch(0.5 0.09 180)"><div class="msn-03__meta"><strong>Falls, long exposure</strong><span>Priya S. · 2.2k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p5" style="--img:url('https://images.unsplash.com/photo-1519125323398-675f0ddb6308?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.07 120);--b:oklch(0.55 0.09 100)"><div class="msn-03__meta"><strong>Park lines</strong><span>Tom H. · 1.9k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p6" style="--img:url('https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 140);--b:oklch(0.42 0.1 160)"><div class="msn-03__meta"><strong>Understory</strong><span>Anya R. · 1.7k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p7" style="--img:url('https://images.unsplash.com/photo-1444723121867-7a241cacace9?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.1 280)"><div class="msn-03__meta"><strong>Skyline, 9pm</strong><span>Ken W. · 1.5k</span></div></a>
<a class="msn-03__card msn-03__card--tall" href="#p8" style="--img:url('https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.08 250);--b:oklch(0.35 0.09 270)"><div class="msn-03__meta"><strong>Midtown canyon</strong><span>Sofia L. · 1.4k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p9" style="--img:url('https://images.unsplash.com/photo-1506744038136-46273834b3fb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.74 0.09 220);--b:oklch(0.48 0.1 240)"><div class="msn-03__meta"><strong>Mirror lake</strong><span>Marc D. · 1.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p10" style="--img:url('https://images.unsplash.com/photo-1472214103451-9374bd1c798e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.09 90);--b:oklch(0.55 0.12 70)"><div class="msn-03__meta"><strong>First cut</strong><span>Jules K. · 1.1k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p11" style="--img:url('https://images.unsplash.com/photo-1470770841072-f978cf4d019e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.08 240);--b:oklch(0.4 0.08 260)"><div class="msn-03__meta"><strong>Cabin, blue hour</strong><span>Priya S. · 980</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p12" style="--img:url('https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=800&auto=format&fit=crop');--a:oklch(0.62 0.07 260);--b:oklch(0.38 0.08 280)"><div class="msn-03__meta"><strong>River city</strong><span>Ken W. · 940</span></div></a>
</div>
</div>
</section><section class="msn-03" aria-label="Responsive variable height masonry photo grid demo">
<div class="msn-03__stage" tabindex="0">
<header class="msn-03__head">
<div><h2>Latest uploads</h2><p>Reading order preserved — newest first</p></div>
<a class="msn-03__cta" href="#submit">Submit a photo</a>
</header>
<div class="msn-03__grid">
<a class="msn-03__card msn-03__card--tall" href="#p1" style="--img:url('https://images.unsplash.com/photo-1469474968028-56623f02e42e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.75 0.1 130);--b:oklch(0.45 0.11 150)"><div class="msn-03__meta"><strong>Valley light</strong><span>Anya R. · 4.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p2" style="--img:url('https://images.unsplash.com/photo-1501594907352-04cda38ebc29?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 40);--b:oklch(0.45 0.13 20)"><div class="msn-03__meta"><strong>Gate in fog</strong><span>Marc D. · 3.1k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p3" style="--img:url('https://images.unsplash.com/photo-1482938289607-e9573fc25ebb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.09 230);--b:oklch(0.45 0.1 250)"><div class="msn-03__meta"><strong>Canyon bend</strong><span>Jules K. · 2.8k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p4" style="--img:url('https://images.unsplash.com/photo-1433086966358-54859d0ed716?q=80&w=800&auto=format&fit=crop');--a:oklch(0.78 0.08 160);--b:oklch(0.5 0.09 180)"><div class="msn-03__meta"><strong>Falls, long exposure</strong><span>Priya S. · 2.2k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p5" style="--img:url('https://images.unsplash.com/photo-1519125323398-675f0ddb6308?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.07 120);--b:oklch(0.55 0.09 100)"><div class="msn-03__meta"><strong>Park lines</strong><span>Tom H. · 1.9k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p6" style="--img:url('https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 140);--b:oklch(0.42 0.1 160)"><div class="msn-03__meta"><strong>Understory</strong><span>Anya R. · 1.7k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p7" style="--img:url('https://images.unsplash.com/photo-1444723121867-7a241cacace9?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.1 280)"><div class="msn-03__meta"><strong>Skyline, 9pm</strong><span>Ken W. · 1.5k</span></div></a>
<a class="msn-03__card msn-03__card--tall" href="#p8" style="--img:url('https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.08 250);--b:oklch(0.35 0.09 270)"><div class="msn-03__meta"><strong>Midtown canyon</strong><span>Sofia L. · 1.4k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p9" style="--img:url('https://images.unsplash.com/photo-1506744038136-46273834b3fb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.74 0.09 220);--b:oklch(0.48 0.1 240)"><div class="msn-03__meta"><strong>Mirror lake</strong><span>Marc D. · 1.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p10" style="--img:url('https://images.unsplash.com/photo-1472214103451-9374bd1c798e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.09 90);--b:oklch(0.55 0.12 70)"><div class="msn-03__meta"><strong>First cut</strong><span>Jules K. · 1.1k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p11" style="--img:url('https://images.unsplash.com/photo-1470770841072-f978cf4d019e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.08 240);--b:oklch(0.4 0.08 260)"><div class="msn-03__meta"><strong>Cabin, blue hour</strong><span>Priya S. · 980</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p12" style="--img:url('https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=800&auto=format&fit=crop');--a:oklch(0.62 0.07 260);--b:oklch(0.38 0.08 280)"><div class="msn-03__meta"><strong>River city</strong><span>Ken W. · 940</span></div></a>
</div>
</div>
</section>.msn-03,
.msn-03 *,
.msn-03 *::before,
.msn-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.msn-03 {
--bg: oklch(0.98 0.004 250);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.55 0.015 260);
--line: oklch(0.9 0.008 250);
--acc: oklch(0.55 0.18 255);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.msn-03__stage {
width: 100%;
container: msn03/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
background: var(--bg);
}
.msn-03__head {
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 16px clamp(16px,3cqi,28px);
background: color-mix(in oklch,var(--bg) 84%,transparent);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.msn-03__head h2 {
font-size: clamp(18px,2.6cqi,24px);
letter-spacing: -.02em;
}
.msn-03__head p {
font-size: 12px;
color: var(--mut);
margin-top: 2px;
}
.msn-03__cta {
display: grid;
place-items: center;
min-height: 40px;
padding: 0 16px;
border-radius: 99px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
color: oklch(0.99 0 0);
background: var(--acc);
transition: background .2s,translate .2s;
}
.msn-03__cta:hover,
.msn-03__cta:focus-visible {
background: oklch(0.48 0.18 255);
translate: 0 -1px;
}
.msn-03__cta:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.msn-03__grid {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fill,minmax(min(210px,100%),1fr));
grid-auto-rows: 10px;
padding: clamp(16px,3cqi,28px);
}
.msn-03__card {
position: relative;
border-radius: 14px;
overflow: clip;
background: linear-gradient(150deg,var(--a),var(--b));
text-decoration: none;
color: oklch(0.99 0 0);
}
.msn-03__card::before {
content: "";
position: absolute;
inset: 0;
background-image: var(--img,none);
background-size: cover;
background-position: center;
transition: scale .45s cubic-bezier(.2,.7,.2,1);
}
.msn-03__card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg,transparent 52%,oklch(0.2 0.02 260/.66));
transition: opacity .3s;
}
.msn-03__card:hover::before,
.msn-03__card:focus-visible::before {
scale: 1.04;
}
.msn-03__card:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.msn-03__card--tall {
grid-row: span 24;
}
.msn-03__card--portrait {
grid-row: span 18;
}
.msn-03__card--square {
grid-row: span 13;
}
.msn-03__card--wide {
grid-row: span 10;
}
.msn-03__meta {
position: absolute;
left: 12px;
right: 12px;
bottom: 10px;
z-index: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.msn-03__meta strong {
font-size: 13.5px;
letter-spacing: -.01em;
text-shadow: 0 1px 8px oklch(0.15 0.02 260/.55);
}
.msn-03__meta span {
font-size: 11.5px;
opacity: .85;
}
@container msn03 (width < 460px) {
.msn-03__grid {
grid-template-columns: repeat(2,1fr);
gap: 8px;
}
.msn-03__card--tall {
grid-row: span 20;
}
.msn-03__card--portrait {
grid-row: span 15;
}
.msn-03__cta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.msn-03 *,
.msn-03 *::before {
transition-duration: .01ms !important;
}
}.msn-03,
.msn-03 *,
.msn-03 *::before,
.msn-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.msn-03 {
--bg: oklch(0.98 0.004 250);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.55 0.015 260);
--line: oklch(0.9 0.008 250);
--acc: oklch(0.55 0.18 255);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.msn-03__stage {
width: 100%;
container: msn03/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
background: var(--bg);
}
.msn-03__head {
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 16px clamp(16px,3cqi,28px);
background: color-mix(in oklch,var(--bg) 84%,transparent);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.msn-03__head h2 {
font-size: clamp(18px,2.6cqi,24px);
letter-spacing: -.02em;
}
.msn-03__head p {
font-size: 12px;
color: var(--mut);
margin-top: 2px;
}
.msn-03__cta {
display: grid;
place-items: center;
min-height: 40px;
padding: 0 16px;
border-radius: 99px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
color: oklch(0.99 0 0);
background: var(--acc);
transition: background .2s,translate .2s;
}
.msn-03__cta:hover,
.msn-03__cta:focus-visible {
background: oklch(0.48 0.18 255);
translate: 0 -1px;
}
.msn-03__cta:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.msn-03__grid {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fill,minmax(min(210px,100%),1fr));
grid-auto-rows: 10px;
padding: clamp(16px,3cqi,28px);
}
.msn-03__card {
position: relative;
border-radius: 14px;
overflow: clip;
background: linear-gradient(150deg,var(--a),var(--b));
text-decoration: none;
color: oklch(0.99 0 0);
}
.msn-03__card::before {
content: "";
position: absolute;
inset: 0;
background-image: var(--img,none);
background-size: cover;
background-position: center;
transition: scale .45s cubic-bezier(.2,.7,.2,1);
}
.msn-03__card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg,transparent 52%,oklch(0.2 0.02 260/.66));
transition: opacity .3s;
}
.msn-03__card:hover::before,
.msn-03__card:focus-visible::before {
scale: 1.04;
}
.msn-03__card:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.msn-03__card--tall {
grid-row: span 24;
}
.msn-03__card--portrait {
grid-row: span 18;
}
.msn-03__card--square {
grid-row: span 13;
}
.msn-03__card--wide {
grid-row: span 10;
}
.msn-03__meta {
position: absolute;
left: 12px;
right: 12px;
bottom: 10px;
z-index: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.msn-03__meta strong {
font-size: 13.5px;
letter-spacing: -.01em;
text-shadow: 0 1px 8px oklch(0.15 0.02 260/.55);
}
.msn-03__meta span {
font-size: 11.5px;
opacity: .85;
}
@container msn03 (width < 460px) {
.msn-03__grid {
grid-template-columns: repeat(2,1fr);
gap: 8px;
}
.msn-03__card--tall {
grid-row: span 20;
}
.msn-03__card--portrait {
grid-row: span 15;
}
.msn-03__cta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.msn-03 *,
.msn-03 *::before {
transition-duration: .01ms !important;
}
}Here's a working CSS Masonry 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 CSS Masonry Photo Grid Variable Height
Source: https://codefronts.com/layouts/css-masonry-layouts/responsive-css-masonry-photo-grid-variable-height/
The masonry technique nobody blogs about and every feed needs: a normal CSS Grid with tiny 10px auto-rows, where each photo spans a quantized number of rows by height class. Unlike multi-column, items flow LEFT-TO-RIGHT — newest first stays newest first for eyes, screen readers and Tab alike — and unlike JavaScript masonry, a resize costs zero script. Cover-cropping absorbs the fractional differences, so the wall stays gapless at every container width.
## HTML
```html
<section class="msn-03" aria-label="Responsive variable height masonry photo grid demo">
<div class="msn-03__stage" tabindex="0">
<header class="msn-03__head">
<div><h2>Latest uploads</h2><p>Reading order preserved — newest first</p></div>
<a class="msn-03__cta" href="#submit">Submit a photo</a>
</header>
<div class="msn-03__grid">
<a class="msn-03__card msn-03__card--tall" href="#p1" style="--img:url('https://images.unsplash.com/photo-1469474968028-56623f02e42e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.75 0.1 130);--b:oklch(0.45 0.11 150)"><div class="msn-03__meta"><strong>Valley light</strong><span>Anya R. · 4.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p2" style="--img:url('https://images.unsplash.com/photo-1501594907352-04cda38ebc29?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 40);--b:oklch(0.45 0.13 20)"><div class="msn-03__meta"><strong>Gate in fog</strong><span>Marc D. · 3.1k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p3" style="--img:url('https://images.unsplash.com/photo-1482938289607-e9573fc25ebb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.09 230);--b:oklch(0.45 0.1 250)"><div class="msn-03__meta"><strong>Canyon bend</strong><span>Jules K. · 2.8k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p4" style="--img:url('https://images.unsplash.com/photo-1433086966358-54859d0ed716?q=80&w=800&auto=format&fit=crop');--a:oklch(0.78 0.08 160);--b:oklch(0.5 0.09 180)"><div class="msn-03__meta"><strong>Falls, long exposure</strong><span>Priya S. · 2.2k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p5" style="--img:url('https://images.unsplash.com/photo-1519125323398-675f0ddb6308?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.07 120);--b:oklch(0.55 0.09 100)"><div class="msn-03__meta"><strong>Park lines</strong><span>Tom H. · 1.9k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p6" style="--img:url('https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 140);--b:oklch(0.42 0.1 160)"><div class="msn-03__meta"><strong>Understory</strong><span>Anya R. · 1.7k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p7" style="--img:url('https://images.unsplash.com/photo-1444723121867-7a241cacace9?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.1 280)"><div class="msn-03__meta"><strong>Skyline, 9pm</strong><span>Ken W. · 1.5k</span></div></a>
<a class="msn-03__card msn-03__card--tall" href="#p8" style="--img:url('https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.08 250);--b:oklch(0.35 0.09 270)"><div class="msn-03__meta"><strong>Midtown canyon</strong><span>Sofia L. · 1.4k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p9" style="--img:url('https://images.unsplash.com/photo-1506744038136-46273834b3fb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.74 0.09 220);--b:oklch(0.48 0.1 240)"><div class="msn-03__meta"><strong>Mirror lake</strong><span>Marc D. · 1.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p10" style="--img:url('https://images.unsplash.com/photo-1472214103451-9374bd1c798e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.09 90);--b:oklch(0.55 0.12 70)"><div class="msn-03__meta"><strong>First cut</strong><span>Jules K. · 1.1k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p11" style="--img:url('https://images.unsplash.com/photo-1470770841072-f978cf4d019e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.08 240);--b:oklch(0.4 0.08 260)"><div class="msn-03__meta"><strong>Cabin, blue hour</strong><span>Priya S. · 980</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p12" style="--img:url('https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=800&auto=format&fit=crop');--a:oklch(0.62 0.07 260);--b:oklch(0.38 0.08 280)"><div class="msn-03__meta"><strong>River city</strong><span>Ken W. · 940</span></div></a>
</div>
</div>
</section>
```
## CSS
```css
.msn-03,
.msn-03 *,
.msn-03 *::before,
.msn-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.msn-03 {
--bg: oklch(0.98 0.004 250);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.55 0.015 260);
--line: oklch(0.9 0.008 250);
--acc: oklch(0.55 0.18 255);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.msn-03__stage {
width: 100%;
container: msn03/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
background: var(--bg);
}
.msn-03__head {
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 16px clamp(16px,3cqi,28px);
background: color-mix(in oklch,var(--bg) 84%,transparent);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.msn-03__head h2 {
font-size: clamp(18px,2.6cqi,24px);
letter-spacing: -.02em;
}
.msn-03__head p {
font-size: 12px;
color: var(--mut);
margin-top: 2px;
}
.msn-03__cta {
display: grid;
place-items: center;
min-height: 40px;
padding: 0 16px;
border-radius: 99px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
color: oklch(0.99 0 0);
background: var(--acc);
transition: background .2s,translate .2s;
}
.msn-03__cta:hover,
.msn-03__cta:focus-visible {
background: oklch(0.48 0.18 255);
translate: 0 -1px;
}
.msn-03__cta:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.msn-03__grid {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fill,minmax(min(210px,100%),1fr));
grid-auto-rows: 10px;
padding: clamp(16px,3cqi,28px);
}
.msn-03__card {
position: relative;
border-radius: 14px;
overflow: clip;
background: linear-gradient(150deg,var(--a),var(--b));
text-decoration: none;
color: oklch(0.99 0 0);
}
.msn-03__card::before {
content: "";
position: absolute;
inset: 0;
background-image: var(--img,none);
background-size: cover;
background-position: center;
transition: scale .45s cubic-bezier(.2,.7,.2,1);
}
.msn-03__card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg,transparent 52%,oklch(0.2 0.02 260/.66));
transition: opacity .3s;
}
.msn-03__card:hover::before,
.msn-03__card:focus-visible::before {
scale: 1.04;
}
.msn-03__card:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.msn-03__card--tall {
grid-row: span 24;
}
.msn-03__card--portrait {
grid-row: span 18;
}
.msn-03__card--square {
grid-row: span 13;
}
.msn-03__card--wide {
grid-row: span 10;
}
.msn-03__meta {
position: absolute;
left: 12px;
right: 12px;
bottom: 10px;
z-index: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.msn-03__meta strong {
font-size: 13.5px;
letter-spacing: -.01em;
text-shadow: 0 1px 8px oklch(0.15 0.02 260/.55);
}
.msn-03__meta span {
font-size: 11.5px;
opacity: .85;
}
@container msn03 (width < 460px) {
.msn-03__grid {
grid-template-columns: repeat(2,1fr);
gap: 8px;
}
.msn-03__card--tall {
grid-row: span 20;
}
.msn-03__card--portrait {
grid-row: span 15;
}
.msn-03__cta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.msn-03 *,
.msn-03 *::before {
transition-duration: .01ms !important;
}
}
```Here's a working CSS Masonry 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 CSS Masonry Photo Grid Variable Height
Source: https://codefronts.com/layouts/css-masonry-layouts/responsive-css-masonry-photo-grid-variable-height/
The masonry technique nobody blogs about and every feed needs: a normal CSS Grid with tiny 10px auto-rows, where each photo spans a quantized number of rows by height class. Unlike multi-column, items flow LEFT-TO-RIGHT — newest first stays newest first for eyes, screen readers and Tab alike — and unlike JavaScript masonry, a resize costs zero script. Cover-cropping absorbs the fractional differences, so the wall stays gapless at every container width.
## HTML
```html
<section class="msn-03" aria-label="Responsive variable height masonry photo grid demo">
<div class="msn-03__stage" tabindex="0">
<header class="msn-03__head">
<div><h2>Latest uploads</h2><p>Reading order preserved — newest first</p></div>
<a class="msn-03__cta" href="#submit">Submit a photo</a>
</header>
<div class="msn-03__grid">
<a class="msn-03__card msn-03__card--tall" href="#p1" style="--img:url('https://images.unsplash.com/photo-1469474968028-56623f02e42e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.75 0.1 130);--b:oklch(0.45 0.11 150)"><div class="msn-03__meta"><strong>Valley light</strong><span>Anya R. · 4.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p2" style="--img:url('https://images.unsplash.com/photo-1501594907352-04cda38ebc29?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 40);--b:oklch(0.45 0.13 20)"><div class="msn-03__meta"><strong>Gate in fog</strong><span>Marc D. · 3.1k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p3" style="--img:url('https://images.unsplash.com/photo-1482938289607-e9573fc25ebb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.09 230);--b:oklch(0.45 0.1 250)"><div class="msn-03__meta"><strong>Canyon bend</strong><span>Jules K. · 2.8k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p4" style="--img:url('https://images.unsplash.com/photo-1433086966358-54859d0ed716?q=80&w=800&auto=format&fit=crop');--a:oklch(0.78 0.08 160);--b:oklch(0.5 0.09 180)"><div class="msn-03__meta"><strong>Falls, long exposure</strong><span>Priya S. · 2.2k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p5" style="--img:url('https://images.unsplash.com/photo-1519125323398-675f0ddb6308?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.07 120);--b:oklch(0.55 0.09 100)"><div class="msn-03__meta"><strong>Park lines</strong><span>Tom H. · 1.9k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p6" style="--img:url('https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.1 140);--b:oklch(0.42 0.1 160)"><div class="msn-03__meta"><strong>Understory</strong><span>Anya R. · 1.7k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p7" style="--img:url('https://images.unsplash.com/photo-1444723121867-7a241cacace9?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.1 280)"><div class="msn-03__meta"><strong>Skyline, 9pm</strong><span>Ken W. · 1.5k</span></div></a>
<a class="msn-03__card msn-03__card--tall" href="#p8" style="--img:url('https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.08 250);--b:oklch(0.35 0.09 270)"><div class="msn-03__meta"><strong>Midtown canyon</strong><span>Sofia L. · 1.4k</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p9" style="--img:url('https://images.unsplash.com/photo-1506744038136-46273834b3fb?q=80&w=800&auto=format&fit=crop');--a:oklch(0.74 0.09 220);--b:oklch(0.48 0.1 240)"><div class="msn-03__meta"><strong>Mirror lake</strong><span>Marc D. · 1.2k</span></div></a>
<a class="msn-03__card msn-03__card--wide" href="#p10" style="--img:url('https://images.unsplash.com/photo-1472214103451-9374bd1c798e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.8 0.09 90);--b:oklch(0.55 0.12 70)"><div class="msn-03__meta"><strong>First cut</strong><span>Jules K. · 1.1k</span></div></a>
<a class="msn-03__card msn-03__card--portrait" href="#p11" style="--img:url('https://images.unsplash.com/photo-1470770841072-f978cf4d019e?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.08 240);--b:oklch(0.4 0.08 260)"><div class="msn-03__meta"><strong>Cabin, blue hour</strong><span>Priya S. · 980</span></div></a>
<a class="msn-03__card msn-03__card--square" href="#p12" style="--img:url('https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=800&auto=format&fit=crop');--a:oklch(0.62 0.07 260);--b:oklch(0.38 0.08 280)"><div class="msn-03__meta"><strong>River city</strong><span>Ken W. · 940</span></div></a>
</div>
</div>
</section>
```
## CSS
```css
.msn-03,
.msn-03 *,
.msn-03 *::before,
.msn-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.msn-03 {
--bg: oklch(0.98 0.004 250);
--ink: oklch(0.23 0.015 260);
--mut: oklch(0.55 0.015 260);
--line: oklch(0.9 0.008 250);
--acc: oklch(0.55 0.18 255);
font-family: 'Segoe UI',system-ui,sans-serif;
color: var(--ink);
container-type: inline-size;
display: block;
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: var(--bg);
}
.msn-03__stage {
width: 100%;
container: msn03/inline-size;
height: 100vh;
height: 100svh;
overflow-y: auto;
background: var(--bg);
}
.msn-03__head {
position: sticky;
top: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
padding: 16px clamp(16px,3cqi,28px);
background: color-mix(in oklch,var(--bg) 84%,transparent);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.msn-03__head h2 {
font-size: clamp(18px,2.6cqi,24px);
letter-spacing: -.02em;
}
.msn-03__head p {
font-size: 12px;
color: var(--mut);
margin-top: 2px;
}
.msn-03__cta {
display: grid;
place-items: center;
min-height: 40px;
padding: 0 16px;
border-radius: 99px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
color: oklch(0.99 0 0);
background: var(--acc);
transition: background .2s,translate .2s;
}
.msn-03__cta:hover,
.msn-03__cta:focus-visible {
background: oklch(0.48 0.18 255);
translate: 0 -1px;
}
.msn-03__cta:focus-visible {
outline: 2px solid var(--acc);
outline-offset: 3px;
}
.msn-03__grid {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fill,minmax(min(210px,100%),1fr));
grid-auto-rows: 10px;
padding: clamp(16px,3cqi,28px);
}
.msn-03__card {
position: relative;
border-radius: 14px;
overflow: clip;
background: linear-gradient(150deg,var(--a),var(--b));
text-decoration: none;
color: oklch(0.99 0 0);
}
.msn-03__card::before {
content: "";
position: absolute;
inset: 0;
background-image: var(--img,none);
background-size: cover;
background-position: center;
transition: scale .45s cubic-bezier(.2,.7,.2,1);
}
.msn-03__card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg,transparent 52%,oklch(0.2 0.02 260/.66));
transition: opacity .3s;
}
.msn-03__card:hover::before,
.msn-03__card:focus-visible::before {
scale: 1.04;
}
.msn-03__card:focus-visible {
outline: 3px solid var(--acc);
outline-offset: 3px;
}
.msn-03__card--tall {
grid-row: span 24;
}
.msn-03__card--portrait {
grid-row: span 18;
}
.msn-03__card--square {
grid-row: span 13;
}
.msn-03__card--wide {
grid-row: span 10;
}
.msn-03__meta {
position: absolute;
left: 12px;
right: 12px;
bottom: 10px;
z-index: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.msn-03__meta strong {
font-size: 13.5px;
letter-spacing: -.01em;
text-shadow: 0 1px 8px oklch(0.15 0.02 260/.55);
}
.msn-03__meta span {
font-size: 11.5px;
opacity: .85;
}
@container msn03 (width < 460px) {
.msn-03__grid {
grid-template-columns: repeat(2,1fr);
gap: 8px;
}
.msn-03__card--tall {
grid-row: span 20;
}
.msn-03__card--portrait {
grid-row: span 15;
}
.msn-03__cta {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.msn-03 *,
.msn-03 *::before {
transition-duration: .01ms !important;
}
}
```How this works
Grid can't auto-pack variable heights (that's demo 12's future), but it can fake it beautifully if you quantize heights onto a fine row lattice:
.grid{ display:grid; gap:10px;
grid-template-columns: repeat(auto-fill, minmax(min(210px,100%), 1fr));
grid-auto-rows: 10px; } /* the lattice */
.card--tall { grid-row: span 24; } /* 24u ≈ 9:16 portrait */
.card--portrait { grid-row: span 18; } /* 18u ≈ 4:5 */
.card--square { grid-row: span 13; }
.card--wide { grid-row: span 10; } /* ≈ 4:3 landscape */One span unit = grid-auto-rows + row-gap = 20px here, so a span-18 card is exactly 18×20−10 = 350px tall. Because heights are deterministic, columns pack tight with no measuring step; because the image is a cover-cropped layer filling the card, a card that is 4 % 'wrong' for its photo crops 4 % instead of leaving a hole. Four height classes are plenty — real photo feeds bucket to portrait/landscape/square anyway.
This is the technique to choose over demo 01's columns whenever ORDER is content: chronological feeds, search results, anything paginated. DOM order, visual order, tab order and screen-reader order all agree, which multicol's down-then-across snake can never promise. It is also append-stable — new cards added at the end never reshuffle existing ones — which is why demo 11 builds infinite scroll on exactly this grid.
Make it yours
- Lattice resolution: 10px rows + 10px gap is the sweet spot; drop to
grid-auto-rows:5px(unit 15px) for finer height steps when your buckets feel samey. - Add a bucket: a hero class with
grid-column: span 2; grid-row: span 20gives you double-wide features — auto-placement backfills around it, and order still reads correctly. - Meta strip height is part of the span math: if you add a taller caption bar, add its extra height ÷ 20 to every span, or move meta inside the image as this demo does.
- Exact ratios instead of crops: give the figure
aspect-ratioand let the card's tail flex — you trade perfect bottoms-alignment for zero cropping; both are legitimate feeds.
Gotchas — read before shipping
- Don't set
grid-auto-rows:1px'for precision' — thousands of implicit 1px rows make style/layout recalc measurably slower on long feeds; 10px is indistinguishable visually and 10× lighter. - The span unit is
auto-rows + row-gap, not auto-rows alone — change the gap and every height silently changes; keep both in one--utoken if you touch them at all. - This is quantized masonry: content of truly unknown height (long text) belongs in demos 04/08's columns or a JS measure step — spans can't guess prose.
auto-fill+minmaxneeds themin(210px,100%)guard or the grid overflows containers narrower than one track — same bug, new layout.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 113+ | 111+ |
Grid row-spans are 2017-universal; oklch and container queries set the stated floor. This exact pattern ships today on production photo feeds at every viewport.