30 CSS Grid Layouts23 / 30

Pure CSSMIT licensed

Mosaic Photo Wall

A hand-set photo mosaic — not a uniform grid, not random masonry, but a designed arrangement: a repeating 6-column composition where spans are chosen so tiles interlock without holes. The organic, editorial look of a curated print wall, with the repeat unit documented so you can extend it infinitely.

Published Updated

Live Demo
Try it

The code

<section class="cgl-23" aria-label="Mosaic photo wall demo">
  <div class="cgl-23__wall">
    <figure class="cgl-23__t" style="--s:3;--r:2;--g:linear-gradient(140deg,oklch(0.62 0.11 140),oklch(0.35 0.07 160))" tabindex="0"><figcaption>Fern gully</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:2;--g:linear-gradient(140deg,oklch(0.7 0.1 90),oklch(0.45 0.08 70))" tabindex="0"><figcaption>Lichen study</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.55 0.09 180),oklch(0.35 0.06 200))" tabindex="0"><figcaption>Creek stone</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.5 0.1 120),oklch(0.3 0.06 140))" tabindex="0"><figcaption>Moss detail</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:3;--g:linear-gradient(180deg,oklch(0.65 0.1 110),oklch(0.3 0.07 150))" tabindex="0"><figcaption>Old-growth trunk, portrait</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:1;--g:linear-gradient(140deg,oklch(0.72 0.09 80),oklch(0.5 0.08 60))" tabindex="0"><figcaption>Dry grass</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:2;--g:linear-gradient(140deg,oklch(0.45 0.08 200),oklch(0.28 0.05 230))" tabindex="0"><figcaption>River bend</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:1;--g:linear-gradient(140deg,oklch(0.58 0.1 130),oklch(0.4 0.07 110))" tabindex="0"><figcaption>Canopy light</figcaption></figure>
    <figure class="cgl-23__t" style="--s:3;--r:1;--g:linear-gradient(90deg,oklch(0.6 0.09 150),oklch(0.38 0.06 170))" tabindex="0"><figcaption>Ridge line, panorama</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.68 0.11 100),oklch(0.44 0.08 90))" tabindex="0"><figcaption>Seed pod</figcaption></figure>
  </div>
</section>
.cgl-23,
.cgl-23 *,
.cgl-23 *::before,
.cgl-23 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-23 {
  width: 100%;
  min-height: 100vh;
  --bg: oklch(0.2 0.02 140);
  font-family: system-ui,sans-serif;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-23 .cgl-23__wall {
  display: grid;
  grid-template-columns: repeat(6,1fr);
  grid-auto-rows: minmax(64px,auto);
  gap: 10px;
}

.cgl-23 .cgl-23__t {
  grid-column: span var(--s,1);
  grid-row: span var(--r,1);
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  background: var(--g);
  cursor: pointer;
}

.cgl-23 .cgl-23__t::after {
  content: "";
  position: absolute;
  inset: 0;
  background: oklch(0.15 0.02 140/.0);
  transition: background .3s ease;
}

.cgl-23 .cgl-23__t:hover::after,
.cgl-23 .cgl-23__t:focus-visible::after {
  background: oklch(0.15 0.02 140/.35);
}

.cgl-23 figcaption {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: 1.4rem .7rem .55rem;
  font-size: .7rem;
  color: oklch(0.97 0.01 120);
  background: linear-gradient(180deg,transparent,oklch(0.12 0.02 140/.8));
  opacity: 0;
  transition: opacity .3s ease;
}

.cgl-23 .cgl-23__t:hover figcaption,
.cgl-23 .cgl-23__t:focus-visible figcaption {
  opacity: 1;
}

.cgl-23 .cgl-23__t:focus-visible {
  outline: 2px solid oklch(0.85 0.12 120);
  outline-offset: 2px;
}

@media (max-width: 520px) {
  .cgl-23 .cgl-23__wall {
    grid-template-columns: repeat(3,1fr);
  }

  .cgl-23 .cgl-23__t {
    --s: min(var(--s-m,var(--s)),3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cgl-23 .cgl-23__t::after,
    .cgl-23 figcaption {
    transition: none;
  }
}
/* No JavaScript — a designed span sequence (rows summing to 6 columns) interlocks the mosaic in source order. */
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 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: Mosaic Photo Wall
Source: https://codefronts.com/layouts/css-grid-layouts/mosaic-photo-wall/

A hand-set photo mosaic — not a uniform grid, not random masonry, but a designed arrangement: a repeating 6-column composition where spans are chosen so tiles interlock without holes. The organic, editorial look of a curated print wall, with the repeat unit documented so you can extend it infinitely.
## HTML
```html
<section class="cgl-23" aria-label="Mosaic photo wall demo">
  <div class="cgl-23__wall">
    <figure class="cgl-23__t" style="--s:3;--r:2;--g:linear-gradient(140deg,oklch(0.62 0.11 140),oklch(0.35 0.07 160))" tabindex="0"><figcaption>Fern gully</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:2;--g:linear-gradient(140deg,oklch(0.7 0.1 90),oklch(0.45 0.08 70))" tabindex="0"><figcaption>Lichen study</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.55 0.09 180),oklch(0.35 0.06 200))" tabindex="0"><figcaption>Creek stone</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.5 0.1 120),oklch(0.3 0.06 140))" tabindex="0"><figcaption>Moss detail</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:3;--g:linear-gradient(180deg,oklch(0.65 0.1 110),oklch(0.3 0.07 150))" tabindex="0"><figcaption>Old-growth trunk, portrait</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:1;--g:linear-gradient(140deg,oklch(0.72 0.09 80),oklch(0.5 0.08 60))" tabindex="0"><figcaption>Dry grass</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:2;--g:linear-gradient(140deg,oklch(0.45 0.08 200),oklch(0.28 0.05 230))" tabindex="0"><figcaption>River bend</figcaption></figure>
    <figure class="cgl-23__t" style="--s:2;--r:1;--g:linear-gradient(140deg,oklch(0.58 0.1 130),oklch(0.4 0.07 110))" tabindex="0"><figcaption>Canopy light</figcaption></figure>
    <figure class="cgl-23__t" style="--s:3;--r:1;--g:linear-gradient(90deg,oklch(0.6 0.09 150),oklch(0.38 0.06 170))" tabindex="0"><figcaption>Ridge line, panorama</figcaption></figure>
    <figure class="cgl-23__t" style="--s:1;--r:1;--g:linear-gradient(140deg,oklch(0.68 0.11 100),oklch(0.44 0.08 90))" tabindex="0"><figcaption>Seed pod</figcaption></figure>
  </div>
</section>
```
## CSS
```css
.cgl-23,
.cgl-23 *,
.cgl-23 *::before,
.cgl-23 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-23 {
  width: 100%;
  min-height: 100vh;
  --bg: oklch(0.2 0.02 140);
  font-family: system-ui,sans-serif;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-23 .cgl-23__wall {
  display: grid;
  grid-template-columns: repeat(6,1fr);
  grid-auto-rows: minmax(64px,auto);
  gap: 10px;
}

.cgl-23 .cgl-23__t {
  grid-column: span var(--s,1);
  grid-row: span var(--r,1);
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  background: var(--g);
  cursor: pointer;
}

.cgl-23 .cgl-23__t::after {
  content: "";
  position: absolute;
  inset: 0;
  background: oklch(0.15 0.02 140/.0);
  transition: background .3s ease;
}

.cgl-23 .cgl-23__t:hover::after,
.cgl-23 .cgl-23__t:focus-visible::after {
  background: oklch(0.15 0.02 140/.35);
}

.cgl-23 figcaption {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: 1.4rem .7rem .55rem;
  font-size: .7rem;
  color: oklch(0.97 0.01 120);
  background: linear-gradient(180deg,transparent,oklch(0.12 0.02 140/.8));
  opacity: 0;
  transition: opacity .3s ease;
}

.cgl-23 .cgl-23__t:hover figcaption,
.cgl-23 .cgl-23__t:focus-visible figcaption {
  opacity: 1;
}

.cgl-23 .cgl-23__t:focus-visible {
  outline: 2px solid oklch(0.85 0.12 120);
  outline-offset: 2px;
}

@media (max-width: 520px) {
  .cgl-23 .cgl-23__wall {
    grid-template-columns: repeat(3,1fr);
  }

  .cgl-23 .cgl-23__t {
    --s: min(var(--s-m,var(--s)),3);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cgl-23 .cgl-23__t::after,
    .cgl-23 figcaption {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — a designed span sequence (rows summing to 6 columns) interlocks the mosaic in source order. */
```

How this works

The wall is repeat(6, 1fr) columns with grid-auto-rows: minmax(64px, auto). Ten tiles form one designed repeat unit: spans of 3×2, 2×2, 1×1, 2×3, 3×1 chosen so every row sums to exactly 6 columns — the interlock is arithmetic, not luck. Append another ten in the same span sequence and the wall extends seamlessly.

Unlike dense packing (demo 24), auto-flow stays in source order — a curator's wall, not an algorithm's. Tiles are <figure> gradients here; in production each holds an <img> with object-fit: cover, and captions surface on hover via the same focus-within-safe reveal as demo 03.

Make it yours

  • Re-compose by editing the span sequence — keep each visual row summing to 6.
  • Scale granularity: 8 or 12 columns give finer interlock options for large walls.
  • Introduce ONE oversized 4×3 statement tile per repeat for hierarchy.
  • Wire tiles to a lightbox — each figure is already focusable and labelled.

Gotchas — read before shipping

  • A span sequence that doesn't sum rows to the column count leaves holes — fix the arithmetic, don't reach for dense (it would reorder your curation).
  • Tall spans (2×3) need portrait-suited imagery; landscape sources crop badly in them.
  • Keep gap modest (8–12px) — mosaic reads as one composition; wide gutters shatter it into cards.

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 57+.

Original-spec grid throughout — the safest visual demo in the collection. oklch gradients are placeholder-only.

Techniques used in this demo

Search CodeFronts

Loading…