12 CSS Masonry Layouts07 / 12

CSS onlyMIT licensed

CSS Grid Masonry Portfolio Showcase Code

An agency-grade work wall where masonry is art direction, not chance: a 2×2 hero, wide and tall tiles at deliberate beats, and grid-auto-flow: dense knitting the field tight around them. Hovering any project dims and desaturates the rest of the wall via one :has() rule — the spotlight effect agencies pay for — and every overlay fires identically for keyboard focus. Dark, cinematic, zero scripts.

Published

Live Demo
Try it

The code

<section class="msn-07" aria-label="CSS grid masonry portfolio showcase demo">
  <div class="msn-07__stage" tabindex="0">
    <header class="msn-07__head">
      <div><h2>Studio Norr — Selected Work</h2><p>Brand, product & film · 2024–2026</p></div>
      <a class="msn-07__cta" href="#contact">Start a project</a>
    </header>
    <div class="msn-07__wall">
      <a class="msn-07__tile msn-07__tile--hero" href="#w1" style="--img:url('https://images.unsplash.com/photo-1561070791-2526d30994b5?q=80&w=1200&auto=format&fit=crop');--a:oklch(0.7 0.12 300);--b:oklch(0.42 0.15 330)"><div class="msn-07__ov"><span class="msn-07__ix">01</span><h3>Meridian Identity</h3><p><span>Brand</span><span>Type design</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w2" style="--img:url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?q=80&w=800&auto=format&fit=crop');--a:oklch(0.65 0.14 280);--b:oklch(0.4 0.13 260)"><div class="msn-07__ov"><span class="msn-07__ix">02</span><h3>Gradient OS</h3><p><span>Product</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--tall" href="#w3" style="--img:url('https://images.unsplash.com/photo-1550745165-9bc0b252726f?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.12 40);--b:oklch(0.35 0.1 20)"><div class="msn-07__ov"><span class="msn-07__ix">03</span><h3>Arcade Rewind</h3><p><span>Campaign</span><span>Film</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w4" style="--img:url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.1 220);--b:oklch(0.45 0.12 250)"><div class="msn-07__ov"><span class="msn-07__ix">04</span><h3>Liquid Type Reel</h3><p><span>Motion</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--wide" href="#w5" style="--img:url('https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=1000&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.09 280)"><div class="msn-07__ov"><span class="msn-07__ix">05</span><h3>Ledger Analytics</h3><p><span>Product</span><span>Data viz</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w6" style="--img:url('https://images.unsplash.com/photo-1541701494587-cb58502866ab?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.13 350);--b:oklch(0.42 0.13 320)"><div class="msn-07__ov"><span class="msn-07__ix">06</span><h3>Bloom Records</h3><p><span>Brand</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w7" style="--img:url('https://images.unsplash.com/photo-1579546929518-9e396f3cc809?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.11 180);--b:oklch(0.45 0.1 160)"><div class="msn-07__ov"><span class="msn-07__ix">07</span><h3>Tidal Streams</h3><p><span>Web</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--tall" href="#w8" style="--img:url('https://images.unsplash.com/photo-1550859492-d5da9d8e45f3?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.14 300);--b:oklch(0.35 0.12 280)"><div class="msn-07__ov"><span class="msn-07__ix">08</span><h3>Nightshift Fashion</h3><p><span>E-comm</span><span>Art dir.</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w9" style="--img:url('https://images.unsplash.com/photo-1498050108023-c5249f4df085?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.08 240);--b:oklch(0.32 0.08 260)"><div class="msn-07__ov"><span class="msn-07__ix">09</span><h3>Compiler Conf</h3><p><span>Event</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
    </div>
  </div>
</section>
.msn-07,
.msn-07 *,
.msn-07 *::before,
.msn-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.msn-07 {
  --bg: oklch(0.17 0.012 285);
  --ink: oklch(0.95 0.008 285);
  --mut: oklch(0.66 0.015 285);
  --line: oklch(0.29 0.015 285);
  --acc: oklch(0.78 0.14 85);
  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-07__stage {
  width: 100%;
  container: msn07/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.msn-07__head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 18px clamp(16px,3cqi,30px);
  background: color-mix(in oklch,var(--bg) 80%,transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.msn-07__head h2 {
  font-size: clamp(17px,2.5cqi,23px);
  letter-spacing: -.02em;
}

.msn-07__head p {
  font-size: 12px;
  color: var(--mut);
  margin-top: 2px;
}

.msn-07__cta {
  display: grid;
  place-items: center;
  min-height: 40px;
  padding: 0 17px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  color: oklch(0.15 0.02 85);
  background: var(--acc);
  transition: background .2s,translate .2s;
}

.msn-07__cta:hover,
.msn-07__cta:focus-visible {
  background: oklch(0.85 0.13 85);
  translate: 0 -1px;
}

.msn-07__cta:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.msn-07__wall {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
  grid-auto-rows: 190px;
  grid-auto-flow: row dense;
  padding: clamp(16px,3cqi,30px);
}

.msn-07__tile {
  position: relative;
  display: block;
  border-radius: 14px;
  overflow: clip;
  text-decoration: none;
  color: var(--ink);
  background: linear-gradient(150deg,var(--a),var(--b));
  transition: opacity .35s,filter .35s,translate .3s;
}

.msn-07__tile::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--img,none);
  background-size: cover;
  background-position: center;
  transition: scale .5s cubic-bezier(.2,.7,.2,1);
}

.msn-07__tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(185deg,transparent 42%,oklch(0.13 0.015 285/.82));
}

.msn-07__tile:hover::before,
.msn-07__tile:focus-visible::before {
  scale: 1.05;
}

.msn-07__tile:focus-visible {
  outline: 3px solid var(--acc);
  outline-offset: 3px;
}

.msn-07__tile--hero {
  grid-area: span 2/span 2;
}

.msn-07__tile--wide {
  grid-column: span 2;
}

.msn-07__tile--tall {
  grid-row: span 2;
}

.msn-07__wall:has(.msn-07__tile:hover) .msn-07__tile:not(:hover),
.msn-07__wall:has(.msn-07__tile:focus-visible) .msn-07__tile:not(:focus-visible) {
  opacity: .45;
  filter: saturate(.55);
}

.msn-07__ov {
  position: absolute;
  left: 16px;
  right: 52px;
  bottom: 14px;
  z-index: 1;
  display: grid;
  gap: 5px;
}

.msn-07__ix {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--acc);
}

.msn-07__ov h3 {
  font-size: clamp(15px,1.8cqi,19px);
  letter-spacing: -.015em;
  text-wrap: balance;
}

.msn-07__tile--hero .msn-07__ov h3 {
  font-size: clamp(19px,2.6cqi,27px);
}

.msn-07__ov p {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.msn-07__ov p span {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .04em;
  padding: 4px 9px;
  border-radius: 99px;
  border: 1px solid oklch(0.95 0.008 285/.28);
  color: oklch(0.9 0.01 285);
  backdrop-filter: blur(3px);
}

.msn-07__go {
  position: absolute;
  right: 14px;
  bottom: 14px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: oklch(0.95 0.008 285/.14);
  backdrop-filter: blur(4px);
  font-size: 15px;
  translate: 0 6px;
  opacity: 0;
  transition: translate .3s,opacity .3s;
}

.msn-07__tile:hover .msn-07__go,
.msn-07__tile:focus-visible .msn-07__go {
  translate: 0 0;
  opacity: 1;
}

@media (hover: none) {
  .msn-07__go {
    opacity: 1;
    translate: 0 0;
  }
}

@container msn07 (width < 520px) {
  .msn-07__wall {
    grid-template-columns: repeat(2,1fr);
    grid-auto-rows: 150px;
    gap: 8px;
  }

  .msn-07__cta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .msn-07 *,
    .msn-07 *::before {
    transition-duration: .01ms !important;
  }
}
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 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: CSS Grid Masonry Portfolio Showcase Code
Source: https://codefronts.com/layouts/css-masonry-layouts/css-grid-masonry-portfolio-showcase-code/

An agency-grade work wall where masonry is art direction, not chance: a 2×2 hero, wide and tall tiles at deliberate beats, and grid-auto-flow: dense knitting the field tight around them. Hovering any project dims and desaturates the rest of the wall via one :has() rule — the spotlight effect agencies pay for — and every overlay fires identically for keyboard focus. Dark, cinematic, zero scripts.
## HTML
```html
<section class="msn-07" aria-label="CSS grid masonry portfolio showcase demo">
  <div class="msn-07__stage" tabindex="0">
    <header class="msn-07__head">
      <div><h2>Studio Norr — Selected Work</h2><p>Brand, product & film · 2024–2026</p></div>
      <a class="msn-07__cta" href="#contact">Start a project</a>
    </header>
    <div class="msn-07__wall">
      <a class="msn-07__tile msn-07__tile--hero" href="#w1" style="--img:url('https://images.unsplash.com/photo-1561070791-2526d30994b5?q=80&w=1200&auto=format&fit=crop');--a:oklch(0.7 0.12 300);--b:oklch(0.42 0.15 330)"><div class="msn-07__ov"><span class="msn-07__ix">01</span><h3>Meridian Identity</h3><p><span>Brand</span><span>Type design</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w2" style="--img:url('https://images.unsplash.com/photo-1557682250-33bd709cbe85?q=80&w=800&auto=format&fit=crop');--a:oklch(0.65 0.14 280);--b:oklch(0.4 0.13 260)"><div class="msn-07__ov"><span class="msn-07__ix">02</span><h3>Gradient OS</h3><p><span>Product</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--tall" href="#w3" style="--img:url('https://images.unsplash.com/photo-1550745165-9bc0b252726f?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.12 40);--b:oklch(0.35 0.1 20)"><div class="msn-07__ov"><span class="msn-07__ix">03</span><h3>Arcade Rewind</h3><p><span>Campaign</span><span>Film</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w4" style="--img:url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=800&auto=format&fit=crop');--a:oklch(0.72 0.1 220);--b:oklch(0.45 0.12 250)"><div class="msn-07__ov"><span class="msn-07__ix">04</span><h3>Liquid Type Reel</h3><p><span>Motion</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--wide" href="#w5" style="--img:url('https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=1000&auto=format&fit=crop');--a:oklch(0.55 0.1 260);--b:oklch(0.32 0.09 280)"><div class="msn-07__ov"><span class="msn-07__ix">05</span><h3>Ledger Analytics</h3><p><span>Product</span><span>Data viz</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w6" style="--img:url('https://images.unsplash.com/photo-1541701494587-cb58502866ab?q=80&w=800&auto=format&fit=crop');--a:oklch(0.68 0.13 350);--b:oklch(0.42 0.13 320)"><div class="msn-07__ov"><span class="msn-07__ix">06</span><h3>Bloom Records</h3><p><span>Brand</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w7" style="--img:url('https://images.unsplash.com/photo-1579546929518-9e396f3cc809?q=80&w=800&auto=format&fit=crop');--a:oklch(0.7 0.11 180);--b:oklch(0.45 0.1 160)"><div class="msn-07__ov"><span class="msn-07__ix">07</span><h3>Tidal Streams</h3><p><span>Web</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile msn-07__tile--tall" href="#w8" style="--img:url('https://images.unsplash.com/photo-1550859492-d5da9d8e45f3?q=80&w=800&auto=format&fit=crop');--a:oklch(0.6 0.14 300);--b:oklch(0.35 0.12 280)"><div class="msn-07__ov"><span class="msn-07__ix">08</span><h3>Nightshift Fashion</h3><p><span>E-comm</span><span>Art dir.</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
      <a class="msn-07__tile" href="#w9" style="--img:url('https://images.unsplash.com/photo-1498050108023-c5249f4df085?q=80&w=800&auto=format&fit=crop');--a:oklch(0.55 0.08 240);--b:oklch(0.32 0.08 260)"><div class="msn-07__ov"><span class="msn-07__ix">09</span><h3>Compiler Conf</h3><p><span>Event</span></p></div><span class="msn-07__go" aria-hidden="true">→</span></a>
    </div>
  </div>
</section>
```
## CSS
```css
.msn-07,
.msn-07 *,
.msn-07 *::before,
.msn-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.msn-07 {
  --bg: oklch(0.17 0.012 285);
  --ink: oklch(0.95 0.008 285);
  --mut: oklch(0.66 0.015 285);
  --line: oklch(0.29 0.015 285);
  --acc: oklch(0.78 0.14 85);
  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-07__stage {
  width: 100%;
  container: msn07/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.msn-07__head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 18px clamp(16px,3cqi,30px);
  background: color-mix(in oklch,var(--bg) 80%,transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.msn-07__head h2 {
  font-size: clamp(17px,2.5cqi,23px);
  letter-spacing: -.02em;
}

.msn-07__head p {
  font-size: 12px;
  color: var(--mut);
  margin-top: 2px;
}

.msn-07__cta {
  display: grid;
  place-items: center;
  min-height: 40px;
  padding: 0 17px;
  border-radius: 99px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  color: oklch(0.15 0.02 85);
  background: var(--acc);
  transition: background .2s,translate .2s;
}

.msn-07__cta:hover,
.msn-07__cta:focus-visible {
  background: oklch(0.85 0.13 85);
  translate: 0 -1px;
}

.msn-07__cta:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.msn-07__wall {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill,minmax(min(220px,100%),1fr));
  grid-auto-rows: 190px;
  grid-auto-flow: row dense;
  padding: clamp(16px,3cqi,30px);
}

.msn-07__tile {
  position: relative;
  display: block;
  border-radius: 14px;
  overflow: clip;
  text-decoration: none;
  color: var(--ink);
  background: linear-gradient(150deg,var(--a),var(--b));
  transition: opacity .35s,filter .35s,translate .3s;
}

.msn-07__tile::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--img,none);
  background-size: cover;
  background-position: center;
  transition: scale .5s cubic-bezier(.2,.7,.2,1);
}

.msn-07__tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(185deg,transparent 42%,oklch(0.13 0.015 285/.82));
}

.msn-07__tile:hover::before,
.msn-07__tile:focus-visible::before {
  scale: 1.05;
}

.msn-07__tile:focus-visible {
  outline: 3px solid var(--acc);
  outline-offset: 3px;
}

.msn-07__tile--hero {
  grid-area: span 2/span 2;
}

.msn-07__tile--wide {
  grid-column: span 2;
}

.msn-07__tile--tall {
  grid-row: span 2;
}

.msn-07__wall:has(.msn-07__tile:hover) .msn-07__tile:not(:hover),
.msn-07__wall:has(.msn-07__tile:focus-visible) .msn-07__tile:not(:focus-visible) {
  opacity: .45;
  filter: saturate(.55);
}

.msn-07__ov {
  position: absolute;
  left: 16px;
  right: 52px;
  bottom: 14px;
  z-index: 1;
  display: grid;
  gap: 5px;
}

.msn-07__ix {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--acc);
}

.msn-07__ov h3 {
  font-size: clamp(15px,1.8cqi,19px);
  letter-spacing: -.015em;
  text-wrap: balance;
}

.msn-07__tile--hero .msn-07__ov h3 {
  font-size: clamp(19px,2.6cqi,27px);
}

.msn-07__ov p {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.msn-07__ov p span {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .04em;
  padding: 4px 9px;
  border-radius: 99px;
  border: 1px solid oklch(0.95 0.008 285/.28);
  color: oklch(0.9 0.01 285);
  backdrop-filter: blur(3px);
}

.msn-07__go {
  position: absolute;
  right: 14px;
  bottom: 14px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: oklch(0.95 0.008 285/.14);
  backdrop-filter: blur(4px);
  font-size: 15px;
  translate: 0 6px;
  opacity: 0;
  transition: translate .3s,opacity .3s;
}

.msn-07__tile:hover .msn-07__go,
.msn-07__tile:focus-visible .msn-07__go {
  translate: 0 0;
  opacity: 1;
}

@media (hover: none) {
  .msn-07__go {
    opacity: 1;
    translate: 0 0;
  }
}

@container msn07 (width < 520px) {
  .msn-07__wall {
    grid-template-columns: repeat(2,1fr);
    grid-auto-rows: 150px;
    gap: 8px;
  }

  .msn-07__cta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .msn-07 *,
    .msn-07 *::before {
    transition-duration: .01ms !important;
  }
}
```

How this works

Curated masonry beats random masonry for showcases. The rhythm is three span classes on a dense grid — then one selector does the theatre:

.wall{ display:grid; gap:10px;
  grid-template-columns: repeat(auto-fill, minmax(min(220px,100%),1fr));
  grid-auto-rows: 190px; grid-auto-flow: row dense; }
.t--hero{ grid-area: span 2 / span 2; }
.t--wide{ grid-column: span 2; }  .t--tall{ grid-row: span 2; }

.wall:has(.tile:hover) .tile:not(:hover){
  opacity:.45; filter:saturate(.55); }   /* the spotlight */

The spotlight rule reads: 'when the wall contains a hovered tile, fade every tile that isn't it.' One declaration, compositor-cheap (opacity + filter), and it degrades to nothing where :has() is unsupported. A twin rule keyed to :focus-within gives keyboard users the same cinema. This is also the pattern's quiet lesson: hover states don't have to live ON the hovered thing — with :has() the CONTAINER can react.

Tiles are stretched-link <a>s: the whole surface navigates, the overlay carries index number, title, tags and a chevron that translates on hover. Fixed grid-auto-rows:190px (not micro-rows like demo 03) is intentional — showcases want the strong horizontal shear lines of aligned tile edges; the mixed spans supply the masonry energy. Dense backfill means you can reorder the featured pieces seasonally by moving one class, and the field re-knits itself.

Make it yours

  • The beat sheet: hero at position 1, wide at ~4, tall at ~6 is the demo's rhythm — move the classes, not the markup order, and dense re-packs around your new beats.
  • Row module: 190px is cinematic 16:9-ish at 220px tracks; 150px turns the wall editorial and dense, 240px makes every tile a poster.
  • Spotlight strength: opacity:.45 / saturate(.55) is assertive; .7/.8 whispers. Keep the transition ≥ .3s or the wall flickers as the pointer crosses gaps.
  • Light theme: swap the four tokens and drop shadow opacities by half — the overlay scrim gradient already adapts because it's built from --ink, not hardcoded black.

Gotchas — read before shipping

  • The spotlight selector must exclude the hovered tile (:not(:hover)) AND pair with a :focus-within twin — dimming the very tile the user is on is a surprisingly common shipped bug.
  • filter on many tiles at once promotes layers; keep the dim to opacity+saturate (cheap) and never blur (expensive at wall scale).
  • Dense + spans can visually reorder — fine for a showcase where narrative order is loose; if your case studies are numbered chapters, drop dense and accept the holes as breathing room.
  • Text over imagery needs the scrim ALWAYS, not just on hover: the tag chips and index sit on a permanent bottom gradient so contrast never depends on pointer position.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by :has(), oklch(), color-mix(), backdrop-filter, @container, text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

:has() sets the Firefox floor (121, Dec 2023); everything else is years older. Without :has() the wall keeps every behavior except the sibling-dim — a textbook progressive enhancement.

Techniques used in this demo

Search CodeFronts

Loading…