ui playground/ progressive blur

Field notes · 07

Harbour Line

Glass, steel and late light. The detail stays up top while the noise falls away below.

f/2.8 · 1/250sDusk
start 35%
end 95%
one masked blur

same stops. same final blur.

28px
0%
Blur toward

Field notes · 07

Harbour Line

Glass, steel and late light. The detail stays up top while the noise falls away below.

f/2.8 · 1/250sDusk
progressive blur
build up the blur…8 layers
view code

React

import { ProgressiveBlur } from "@/components/progressive-blur";

<ProgressiveBlur
  layers={8}
  start={35}
  end={95}
  maxBlur={28}
  curve={[0.4, 0.02, 0.74, 0.96]}
/>

HTML + CSS

<!-- Progressive blur: 8 layers, 35% → 95% to bottom, 28px total.
     Stacked backdrop blurs compound, so each layer only adds the blur still missing. -->
<div class="card">
  <img class="card-img" src="your-image.jpg" alt="">
  <div class="progressive-blur" aria-hidden="true">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>
  <div class="card-underlay"></div>
  <div class="card-text">
    <p class="card-eyebrow">Field notes · 07</p>
    <h3 class="card-title">Harbour Line</h3>
    <p class="card-body">Glass, steel and late light. The detail stays up top while the noise falls away below.</p>
    <div class="card-foot"><span>f/2.8 · 1/250s</span><span>Dusk</span></div>
  </div>
</div>

<style>
.card {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 28px;
  overflow: hidden;
  container-type: inline-size;
}
.card-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 50%;
}
.progressive-blur { position: absolute; inset: 0; pointer-events: none; }
.progressive-blur > div { position: absolute; inset: 0; }
.progressive-blur > div:nth-child(1) {
  -webkit-backdrop-filter: blur(1.0px);
  backdrop-filter: blur(1.0px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 35%, #000 42.5%);
  mask-image: linear-gradient(to bottom, transparent 35%, #000 42.5%);
}
.progressive-blur > div:nth-child(2) {
  -webkit-backdrop-filter: blur(3.3px);
  backdrop-filter: blur(3.3px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 42.5%, #000 50%);
  mask-image: linear-gradient(to bottom, transparent 42.5%, #000 50%);
}
.progressive-blur > div:nth-child(3) {
  -webkit-backdrop-filter: blur(6.2px);
  backdrop-filter: blur(6.2px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 50%, #000 57.5%);
  mask-image: linear-gradient(to bottom, transparent 50%, #000 57.5%);
}
.progressive-blur > div:nth-child(4) {
  -webkit-backdrop-filter: blur(9.3px);
  backdrop-filter: blur(9.3px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 57.5%, #000 65%);
  mask-image: linear-gradient(to bottom, transparent 57.5%, #000 65%);
}
.progressive-blur > div:nth-child(5) {
  -webkit-backdrop-filter: blur(12.0px);
  backdrop-filter: blur(12.0px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 65%, #000 72.5%);
  mask-image: linear-gradient(to bottom, transparent 65%, #000 72.5%);
}
.progressive-blur > div:nth-child(6) {
  -webkit-backdrop-filter: blur(13.9px);
  backdrop-filter: blur(13.9px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 72.5%, #000 80%);
  mask-image: linear-gradient(to bottom, transparent 72.5%, #000 80%);
}
.progressive-blur > div:nth-child(7) {
  -webkit-backdrop-filter: blur(14.0px);
  backdrop-filter: blur(14.0px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 80%, #000 87.5%);
  mask-image: linear-gradient(to bottom, transparent 80%, #000 87.5%);
}
.progressive-blur > div:nth-child(8) {
  -webkit-backdrop-filter: blur(10.6px);
  backdrop-filter: blur(10.6px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 87.5%, #000 95%);
  mask-image: linear-gradient(to bottom, transparent 87.5%, #000 95%);
}
.card-underlay {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, rgba(0,0,0,0) 35%, rgba(0,0,0,.5) 100%);
}
.card-text { position: absolute; left: 0; right: 0; bottom: 0; padding: 0 7cqw 6cqw; color: #fff; }
.card-eyebrow { margin: 0 0 1.6cqw; font: 600 2.7cqw/1.2 system-ui, sans-serif; letter-spacing: .18em; text-transform: uppercase; color: #ff9a7a; }
.card-title { margin: 0 0 2.6cqw; font: 400 12.5cqw/.95 "Instrument Serif", Georgia, serif; }
.card-body { margin: 0 0 6cqw; font: 400 3.6cqw/1.45 system-ui, sans-serif; opacity: .88; }
.card-foot { display: flex; justify-content: space-between; font: 500 2.4cqw/1 ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; opacity: .62; }
</style>