22 CSS Image Galleries07 / 22
CSS Masonry Image Gallery
The Pinterest classic: staggered vertical columns where images of different heights pack tightly together with no awkward gaps, and no JavaScript layout library. The single most-searched gallery pattern in Tier-1 markets.
Published Updated
The code
<section class="gl-01" aria-label="Masonry photo gallery">
<div class="gl-01__grid">
<figure class="gl-01__item"><img src="https://loremflickr.com/500/700/mountain?lock=101" alt="Snow-capped mountain ridge at dawn" loading="lazy" decoding="async" width="500" height="700"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/400/forest?lock=102" alt="Misty pine forest canopy" loading="lazy" decoding="async" width="500" height="400"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/560/lake?lock=103" alt="Still alpine lake reflecting the sky" loading="lazy" decoding="async" width="500" height="560"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/620/desert?lock=104" alt="Rolling desert sand dunes" loading="lazy" decoding="async" width="500" height="620"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/380/ocean?lock=105" alt="Turquoise ocean waves from above" loading="lazy" decoding="async" width="500" height="380"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/680/waterfall?lock=106" alt="Tall waterfall in a green gorge" loading="lazy" decoding="async" width="500" height="680"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/460/city?lock=107" alt="City skyline at blue hour" loading="lazy" decoding="async" width="500" height="460"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/540/autumn?lock=108" alt="Autumn trees in gold and red" loading="lazy" decoding="async" width="500" height="540"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/420/canyon?lock=109" alt="Layered red canyon walls" loading="lazy" decoding="async" width="500" height="420"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/600/aurora?lock=110" alt="Green aurora over a snowy valley" loading="lazy" decoding="async" width="500" height="600"></figure>
</div>
</section><section class="gl-01" aria-label="Masonry photo gallery">
<div class="gl-01__grid">
<figure class="gl-01__item"><img src="https://loremflickr.com/500/700/mountain?lock=101" alt="Snow-capped mountain ridge at dawn" loading="lazy" decoding="async" width="500" height="700"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/400/forest?lock=102" alt="Misty pine forest canopy" loading="lazy" decoding="async" width="500" height="400"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/560/lake?lock=103" alt="Still alpine lake reflecting the sky" loading="lazy" decoding="async" width="500" height="560"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/620/desert?lock=104" alt="Rolling desert sand dunes" loading="lazy" decoding="async" width="500" height="620"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/380/ocean?lock=105" alt="Turquoise ocean waves from above" loading="lazy" decoding="async" width="500" height="380"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/680/waterfall?lock=106" alt="Tall waterfall in a green gorge" loading="lazy" decoding="async" width="500" height="680"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/460/city?lock=107" alt="City skyline at blue hour" loading="lazy" decoding="async" width="500" height="460"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/540/autumn?lock=108" alt="Autumn trees in gold and red" loading="lazy" decoding="async" width="500" height="540"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/420/canyon?lock=109" alt="Layered red canyon walls" loading="lazy" decoding="async" width="500" height="420"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/600/aurora?lock=110" alt="Green aurora over a snowy valley" loading="lazy" decoding="async" width="500" height="600"></figure>
</div>
</section>.gl-01,
.gl-01 *,
.gl-01 *::before,
.gl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-01 ::selection {
background: oklch(0.6 0.16 250);
color: #fff;
}
.gl-01 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #f4f5f7;
display: flex;
align-items: flex-start;
justify-content: center;
}
.gl-01__grid {
width: min(920px,100%);
columns: 4;
column-gap: 14px;
}
.gl-01__item {
break-inside: avoid;
margin: 0 0 14px;
border-radius: 12px;
overflow: hidden;
background: #e7eaef;
box-shadow: 0 8px 22px -16px rgba(16,24,40,.6);
transition: box-shadow .3s;
}
.gl-01__item:hover {
box-shadow: 0 16px 34px -18px rgba(16,24,40,.55);
}
.gl-01__item img {
display: block;
width: 100%;
height: auto;
transform: scale(1);
transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
.gl-01__item:hover img,
.gl-01__item:focus-within img {
transform: scale(1.06);
}
.gl-01__item a:focus-visible {
outline: 3px solid oklch(0.6 0.16 250);
outline-offset: 3px;
}
@media (max-width: 820px) {
.gl-01__grid {
columns: 3;
}
}
@media (max-width: 560px) {
.gl-01__grid {
columns: 2;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-01__item,
.gl-01__item img {
transition: none;
}
}.gl-01,
.gl-01 *,
.gl-01 *::before,
.gl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-01 ::selection {
background: oklch(0.6 0.16 250);
color: #fff;
}
.gl-01 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #f4f5f7;
display: flex;
align-items: flex-start;
justify-content: center;
}
.gl-01__grid {
width: min(920px,100%);
columns: 4;
column-gap: 14px;
}
.gl-01__item {
break-inside: avoid;
margin: 0 0 14px;
border-radius: 12px;
overflow: hidden;
background: #e7eaef;
box-shadow: 0 8px 22px -16px rgba(16,24,40,.6);
transition: box-shadow .3s;
}
.gl-01__item:hover {
box-shadow: 0 16px 34px -18px rgba(16,24,40,.55);
}
.gl-01__item img {
display: block;
width: 100%;
height: auto;
transform: scale(1);
transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
.gl-01__item:hover img,
.gl-01__item:focus-within img {
transform: scale(1.06);
}
.gl-01__item a:focus-visible {
outline: 3px solid oklch(0.6 0.16 250);
outline-offset: 3px;
}
@media (max-width: 820px) {
.gl-01__grid {
columns: 3;
}
}
@media (max-width: 560px) {
.gl-01__grid {
columns: 2;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-01__item,
.gl-01__item img {
transition: none;
}
}/* No JavaScript — CSS multi-column + break-inside:avoid produce the masonry packing. */
/* No JavaScript — CSS multi-column + break-inside:avoid produce the masonry packing. */Here's a working CSS Image Gallerie 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: CSS Masonry Image Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-masonry-image-gallery/
The Pinterest classic: staggered vertical columns where images of different heights pack tightly together with no awkward gaps, and no JavaScript layout library. The single most-searched gallery pattern in Tier-1 markets.
## HTML
```html
<section class="gl-01" aria-label="Masonry photo gallery">
<div class="gl-01__grid">
<figure class="gl-01__item"><img src="https://loremflickr.com/500/700/mountain?lock=101" alt="Snow-capped mountain ridge at dawn" loading="lazy" decoding="async" width="500" height="700"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/400/forest?lock=102" alt="Misty pine forest canopy" loading="lazy" decoding="async" width="500" height="400"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/560/lake?lock=103" alt="Still alpine lake reflecting the sky" loading="lazy" decoding="async" width="500" height="560"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/620/desert?lock=104" alt="Rolling desert sand dunes" loading="lazy" decoding="async" width="500" height="620"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/380/ocean?lock=105" alt="Turquoise ocean waves from above" loading="lazy" decoding="async" width="500" height="380"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/680/waterfall?lock=106" alt="Tall waterfall in a green gorge" loading="lazy" decoding="async" width="500" height="680"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/460/city?lock=107" alt="City skyline at blue hour" loading="lazy" decoding="async" width="500" height="460"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/540/autumn?lock=108" alt="Autumn trees in gold and red" loading="lazy" decoding="async" width="500" height="540"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/420/canyon?lock=109" alt="Layered red canyon walls" loading="lazy" decoding="async" width="500" height="420"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/600/aurora?lock=110" alt="Green aurora over a snowy valley" loading="lazy" decoding="async" width="500" height="600"></figure>
</div>
</section>
```
## CSS
```css
.gl-01,
.gl-01 *,
.gl-01 *::before,
.gl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-01 ::selection {
background: oklch(0.6 0.16 250);
color: #fff;
}
.gl-01 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #f4f5f7;
display: flex;
align-items: flex-start;
justify-content: center;
}
.gl-01__grid {
width: min(920px,100%);
columns: 4;
column-gap: 14px;
}
.gl-01__item {
break-inside: avoid;
margin: 0 0 14px;
border-radius: 12px;
overflow: hidden;
background: #e7eaef;
box-shadow: 0 8px 22px -16px rgba(16,24,40,.6);
transition: box-shadow .3s;
}
.gl-01__item:hover {
box-shadow: 0 16px 34px -18px rgba(16,24,40,.55);
}
.gl-01__item img {
display: block;
width: 100%;
height: auto;
transform: scale(1);
transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
.gl-01__item:hover img,
.gl-01__item:focus-within img {
transform: scale(1.06);
}
.gl-01__item a:focus-visible {
outline: 3px solid oklch(0.6 0.16 250);
outline-offset: 3px;
}
@media (max-width: 820px) {
.gl-01__grid {
columns: 3;
}
}
@media (max-width: 560px) {
.gl-01__grid {
columns: 2;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-01__item,
.gl-01__item img {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — CSS multi-column + break-inside:avoid produce the masonry packing. */
```Here's a working CSS Image Gallerie 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: CSS Masonry Image Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-masonry-image-gallery/
The Pinterest classic: staggered vertical columns where images of different heights pack tightly together with no awkward gaps, and no JavaScript layout library. The single most-searched gallery pattern in Tier-1 markets.
## HTML
```html
<section class="gl-01" aria-label="Masonry photo gallery">
<div class="gl-01__grid">
<figure class="gl-01__item"><img src="https://loremflickr.com/500/700/mountain?lock=101" alt="Snow-capped mountain ridge at dawn" loading="lazy" decoding="async" width="500" height="700"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/400/forest?lock=102" alt="Misty pine forest canopy" loading="lazy" decoding="async" width="500" height="400"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/560/lake?lock=103" alt="Still alpine lake reflecting the sky" loading="lazy" decoding="async" width="500" height="560"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/620/desert?lock=104" alt="Rolling desert sand dunes" loading="lazy" decoding="async" width="500" height="620"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/380/ocean?lock=105" alt="Turquoise ocean waves from above" loading="lazy" decoding="async" width="500" height="380"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/680/waterfall?lock=106" alt="Tall waterfall in a green gorge" loading="lazy" decoding="async" width="500" height="680"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/460/city?lock=107" alt="City skyline at blue hour" loading="lazy" decoding="async" width="500" height="460"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/540/autumn?lock=108" alt="Autumn trees in gold and red" loading="lazy" decoding="async" width="500" height="540"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/420/canyon?lock=109" alt="Layered red canyon walls" loading="lazy" decoding="async" width="500" height="420"></figure>
<figure class="gl-01__item"><img src="https://loremflickr.com/500/600/aurora?lock=110" alt="Green aurora over a snowy valley" loading="lazy" decoding="async" width="500" height="600"></figure>
</div>
</section>
```
## CSS
```css
.gl-01,
.gl-01 *,
.gl-01 *::before,
.gl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-01 ::selection {
background: oklch(0.6 0.16 250);
color: #fff;
}
.gl-01 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #f4f5f7;
display: flex;
align-items: flex-start;
justify-content: center;
}
.gl-01__grid {
width: min(920px,100%);
columns: 4;
column-gap: 14px;
}
.gl-01__item {
break-inside: avoid;
margin: 0 0 14px;
border-radius: 12px;
overflow: hidden;
background: #e7eaef;
box-shadow: 0 8px 22px -16px rgba(16,24,40,.6);
transition: box-shadow .3s;
}
.gl-01__item:hover {
box-shadow: 0 16px 34px -18px rgba(16,24,40,.55);
}
.gl-01__item img {
display: block;
width: 100%;
height: auto;
transform: scale(1);
transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
.gl-01__item:hover img,
.gl-01__item:focus-within img {
transform: scale(1.06);
}
.gl-01__item a:focus-visible {
outline: 3px solid oklch(0.6 0.16 250);
outline-offset: 3px;
}
@media (max-width: 820px) {
.gl-01__grid {
columns: 3;
}
}
@media (max-width: 560px) {
.gl-01__grid {
columns: 2;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-01__item,
.gl-01__item img {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — CSS multi-column + break-inside:avoid produce the masonry packing. */
```How this works
The gallery is laid out with CSS multi-column (columns: 4 + column-gap). Each figure is told break-inside: avoid so an image is never split across a column break, and images keep their natural heights, which is what produces the classic staggered masonry rhythm.
Because columns flow top-to-bottom then left-to-right, reading order is preserved for screen readers, and every tile is a semantic <figure> with an <img alt>. A subtle contained hover-zoom (clipped by overflow:hidden) adds life without any reflow.
Where you want row-first reading order instead, progressive-enhance to native grid-template-rows: masonry in supporting engines — the columns version is the universally-supported baseline shown here.
Make it yours
- Change the column count by editing
columns(or setcolumn-widthfor auto-responsive columns). - Tune density with
column-gapand the figure'smargin-bottom. - Swap the hover-zoom depth via the image
transform: scale()value. - Progressive-enhance to
grid-template-rows: masonrybehind an@supportsquery for row-order masonry.
Gotchas — read before shipping
- Multi-column fills column-by-column, so visual order runs down each column, not left-to-right across a row — fine for photos, worth knowing for ordered content.
- Always set
break-inside: avoidon the items or an image can tear across the column boundary. - Give each
<img>width/height or anaspect-ratioso the column heights are known before images load (no CLS).
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 50+.
CSS multi-column is supported everywhere. Native grid masonry is still experimental (Firefox flag / early Safari) so this baseline uses columns.