22 CSS Image Galleries07 / 22

Pure CSSMIT licensed

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

Live Demo
Try it

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>
.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. */
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 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 set column-width for auto-responsive columns).
  • Tune density with column-gap and the figure's margin-bottom.
  • Swap the hover-zoom depth via the image transform: scale() value.
  • Progressive-enhance to grid-template-rows: masonry behind an @supports query 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: avoid on the items or an image can tear across the column boundary.
  • Give each <img> width/height or an aspect-ratio so the column heights are known before images load (no CLS).

Browser support

ChromeSafariFirefoxEdge
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.

Techniques used in this demo

Search CodeFronts

Loading…