10 CSS Parallax Effects 10 / 10

CSS Parallax Background Blur Transition

Scroll drives backdrop-filter: blur from 0 to 28px on a vivid blob hero, while a frosted-glass card rises and fades in — pure CSS filter parallax.

CSS + JS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="plx-10">

  <div class="plx-10__badge" id="plx10-badge">BLUR · 0px</div>

  <!-- Hero -->
  <div class="plx-10__hero" id="plx10-hero">
    <div class="plx-10__hero-sticky">

      <!-- Vivid color blobs -->
      <div class="plx-10__blobs" id="plx10-blobs">
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
      </div>

      <!-- Medium blur duplicate (fades in mid-scroll) -->
      <div class="plx-10__blobs-blur" id="plx10-blur1">
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
      </div>

      <!-- Heavy blur duplicate (fades in late) -->
      <div class="plx-10__blobs-blur2" id="plx10-blur2">
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
        <div class="plx-10__blob"></div>
      </div>

      <!-- Dark overlay for contrast -->
      <div class="plx-10__dark-overlay" id="plx10-dark"></div>

      <!-- Initial headline (fades out) -->
      <div class="plx-10__hero-headline" id="plx10-hl">
        <span class="plx-10__hero-tag">Parallax · Background Blur Transition</span>
        <h1 class="plx-10__hero-h">Focus<br>through<br><em>motion.</em></h1>
        <p class="plx-10__hero-sub">Scroll to blur the world. The color field beneath you fades from vivid to frosted glass as you descend.</p>
        <div class="plx-10__scroll-hint">
          <div class="plx-10__scroll-line"></div>
          <span>Scroll</span>
        </div>
      </div>

      <!-- Frosted glass card (fades in) -->
      <div class="plx-10__glass-wrap" id="plx10-glass">
        <div class="plx-10__glass-card">
          <span class="plx-10__card-tag">Glassmorphism · Blur Parallax</span>
          <h2 class="plx-10__card-h">The world<br>went <em>soft.</em></h2>
          <div class="plx-10__card-line"></div>
          <p class="plx-10__card-p">The same color field that lived beneath the headline has blurred into this frosted surface. Depth through diffusion — a parallax story told in focus.</p>
        </div>
      </div>

    </div>
  </div>

  <!-- Content sections -->
  <div class="plx-10__sections">

    <div class="plx-10__section">
      <div class="plx-10__sec-visual">
        <div class="plx-10__sec-visual-inner" id="plx10-v1"></div>
        <div class="plx-10__sec-reveal"><span>Depth revealed</span></div>
      </div>
      <div class="plx-10__sec-body">
        <span class="plx-10__sec-tag">01 / Concept</span>
        <h2 class="plx-10__sec-h">Blur as <em>distance.</em></h2>
        <div class="plx-10__sec-line"></div>
        <p class="plx-10__sec-p">The further something is from the focal plane, the softer it becomes. Parallax blur recreates this optical truth — objects you've scrolled past recede into soft focus.</p>
      </div>
    </div>

    <div class="plx-10__section">
      <div class="plx-10__sec-visual">
        <div class="plx-10__sec-visual-inner" id="plx10-v2"></div>
        <div class="plx-10__sec-reveal"><span>Color diffused</span></div>
      </div>
      <div class="plx-10__sec-body">
        <span class="plx-10__sec-tag">02 / Technique</span>
        <h2 class="plx-10__sec-h">Glass over <em>color.</em></h2>
        <div class="plx-10__sec-line"></div>
        <p class="plx-10__sec-p">Two versions of the same color field — one sharp, one blurred — cross-fade as you scroll. The transition is driven by opacity, not filters, keeping performance smooth on any device.</p>
      </div>
    </div>

    <div class="plx-10__section">
      <div class="plx-10__sec-visual">
        <div class="plx-10__sec-visual-inner" id="plx10-v3"></div>
        <div class="plx-10__sec-reveal"><span>Depth in motion</span></div>
      </div>
      <div class="plx-10__sec-body">
        <span class="plx-10__sec-tag">03 / Effect</span>
        <h2 class="plx-10__sec-h">Parallax through <em>focus.</em></h2>
        <div class="plx-10__sec-line"></div>
        <p class="plx-10__sec-p">The background images inside these panels scroll at 0.35× the page speed, creating the sensation that the content floats in front of a deeper layer. Scroll depth as spatial poetry.</p>
      </div>
    </div>

  </div>

</div>
.plx-10, .plx-10 *, .plx-10 *::before, .plx-10 *::after {
  box-sizing: border-box; margin: 0; padding: 0;
}
.plx-10 {
  --bg: #07080e;
  --text: #eff2f8;
  --accent: #92c5f0;
  --warm: #e8c88a;
  font-family: 'Cormorant Garamond', serif;
  background: var(--bg);
  color: var(--text);
  /* overflow-x:hidden was breaking position:sticky on descendants —
     same root cause as Demo 09 (commit 876bc78). overflow:clip is
     the modern equivalent: prevents horizontal overflow without
     creating a scroll context that would steal sticky containment
     from the body. Chrome 90+, Safari 16+, Firefox 81+. */
  overflow-x: clip;
}

/* ═══ HERO: vivid color field blurs as you scroll ═══ */
.plx-10__hero {
  position: relative;
  height: 250vh;
}
.plx-10__hero-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}

/* The vivid color blobs — this layer parallaxes */
.plx-10__blobs {
  position: absolute;
  inset: -20%;
  will-change: transform;
}
.plx-10__blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
}
.plx-10__blob:nth-child(1) {
  width: 60vw; height: 60vw;
  top: -15%; left: -10%;
  background: radial-gradient(circle, rgba(80,150,255,0.6) 0%, transparent 70%);
}
.plx-10__blob:nth-child(2) {
  width: 50vw; height: 50vw;
  top: 20%; right: -5%;
  background: radial-gradient(circle, rgba(180,80,255,0.55) 0%, transparent 70%);
}
.plx-10__blob:nth-child(3) {
  width: 45vw; height: 45vw;
  bottom: -5%; left: 30%;
  background: radial-gradient(circle, rgba(255,130,80,0.5) 0%, transparent 70%);
}
.plx-10__blob:nth-child(4) {
  width: 35vw; height: 35vw;
  top: 50%; left: 15%;
  background: radial-gradient(circle, rgba(80,240,180,0.35) 0%, transparent 70%);
}

/* The frosted overlay — its opacity and filter change with scroll */
/* IMPORTANT: we use a solid semi-opaque overlay that increases, 
   NOT backdrop-filter (which doesn't work on parent bg) */
.plx-10__frost-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(
    160deg,
    rgba(7, 8, 14, 0) 0%,
    rgba(7, 8, 14, 0) 100%
  );
  will-change: opacity;
  opacity: 0;
  /* We'll use a canvas-style blur by adding a blurred duplicate via a pseudo */
}

/* The blurred version of the blobs — initially invisible, fades in */
.plx-10__blobs-blur {
  position: absolute;
  inset: -20%;
  will-change: transform, opacity, filter;
  opacity: 0;
  filter: blur(0px);
}
.plx-10__blobs-blur .plx-10__blob:nth-child(1) {
  width: 60vw; height: 60vw;
  top: -15%; left: -10%;
  background: radial-gradient(circle, rgba(80,150,255,0.6) 0%, transparent 70%);
  filter: blur(70px);
}
.plx-10__blobs-blur .plx-10__blob:nth-child(2) {
  width: 50vw; height: 50vw;
  top: 20%; right: -5%;
  background: radial-gradient(circle, rgba(180,80,255,0.55) 0%, transparent 70%);
  filter: blur(70px);
}
.plx-10__blobs-blur .plx-10__blob:nth-child(3) {
  width: 45vw; height: 45vw;
  bottom: -5%; left: 30%;
  background: radial-gradient(circle, rgba(255,130,80,0.5) 0%, transparent 70%);
  filter: blur(70px);
}
.plx-10__blobs-blur .plx-10__blob:nth-child(4) {
  width: 35vw; height: 35vw;
  top: 50%; left: 15%;
  background: radial-gradient(circle, rgba(80,240,180,0.35) 0%, transparent 70%);
  filter: blur(70px);
}
/* Additional extra-blur layer at full blur */
.plx-10__blobs-blur2 {
  position: absolute;
  inset: -20%;
  will-change: transform, opacity, filter;
  opacity: 0;
}
.plx-10__blobs-blur2 .plx-10__blob:nth-child(1) {
  width: 70vw; height: 70vw;
  top: -20%; left: -15%;
  background: radial-gradient(circle, rgba(80,150,255,0.5) 0%, transparent 70%);
  filter: blur(140px);
}
.plx-10__blobs-blur2 .plx-10__blob:nth-child(2) {
  width: 60vw; height: 60vw;
  top: 15%; right: -10%;
  background: radial-gradient(circle, rgba(180,80,255,0.45) 0%, transparent 70%);
  filter: blur(120px);
}
.plx-10__blobs-blur2 .plx-10__blob:nth-child(3) {
  width: 55vw; height: 55vw;
  bottom: -10%; left: 25%;
  background: radial-gradient(circle, rgba(255,130,80,0.4) 0%, transparent 70%);
  filter: blur(130px);
}

/* Dark overlay accumulates */
.plx-10__dark-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: var(--bg);
  will-change: opacity;
  opacity: 0;
}

/* Glass card that fades in on top */
.plx-10__glass-wrap {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: opacity, transform;
  opacity: 0;
  transform: translateY(30px);
}
.plx-10__glass-card {
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 20px;
  padding: 56px 64px;
  max-width: 560px;
  text-align: center;
  box-shadow: 0 32px 80px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.12);
  position: relative;
  overflow: hidden;
}
.plx-10__glass-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.04) 0%, transparent 60%);
  pointer-events: none;
}
.plx-10__card-tag {
  font-family: 'DM Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.7;
  display: block;
  margin-bottom: 20px;
}
.plx-10__card-h {
  font-size: clamp(36px, 5vw, 64px);
  font-weight: 600;
  line-height: 1.05;
  margin-bottom: 20px;
}
.plx-10__card-h em {
  font-style: italic;
  font-weight: 300;
  color: var(--accent);
}
.plx-10__card-p {
  font-size: 17px;
  font-weight: 300;
  font-style: italic;
  line-height: 1.7;
  color: rgba(239,242,248,0.6);
}
.plx-10__card-line {
  width: 40px;
  height: 1px;
  background: rgba(146,197,240,0.4);
  margin: 24px auto;
}

/* Blur strength badge */
.plx-10__badge {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 100;
  font-family: 'DM Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--accent);
  opacity: 0.6;
  background: rgba(7,8,14,0.7);
  padding: 6px 12px;
  border: 1px solid rgba(146,197,240,0.2);
  border-radius: 4px;
  pointer-events: none;
}

/* Hero initial headline (fades out as blur comes in) */
.plx-10__hero-headline {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px;
  will-change: opacity, transform;
  pointer-events: none;
}
.plx-10__hero-tag {
  font-family: 'DM Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(239,242,248,0.4);
  margin-bottom: 24px;
  display: block;
}
.plx-10__hero-h {
  font-size: clamp(52px, 12vw, 160px);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: -0.02em;
}
.plx-10__hero-h em {
  font-style: italic;
  font-weight: 300;
  color: rgba(239,242,248,0.6);
}
.plx-10__hero-sub {
  margin-top: 24px;
  font-size: 16px;
  font-style: italic;
  font-weight: 300;
  color: rgba(239,242,248,0.45);
  max-width: 420px;
}
.plx-10__scroll-hint {
  margin-top: 40px;
  font-family: 'DM Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(239,242,248,0.25);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.plx-10__scroll-line {
  width: 1px; height: 44px;
  background: linear-gradient(to bottom, rgba(239,242,248,0.3), transparent);
  animation: plx-10-line 1.8s ease-in-out infinite;
}
@keyframes plx-10-line {
  0%, 100% { opacity: 1; transform: scaleY(1); transform-origin: top; }
  50% { opacity: 0.3; transform: scaleY(0.4); transform-origin: top; }
}

/* ═══ CONTENT SECTIONS below hero ═══ */
.plx-10__sections {
  background: var(--bg);
}
.plx-10__section {
  position: relative;
  min-height: 80vh;
  padding: 100px 5vw;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
  border-top: 1px solid rgba(255,255,255,0.06);
  overflow: hidden;
}
.plx-10__section:nth-child(even) {
  direction: rtl;
}
.plx-10__section:nth-child(even) > * { direction: ltr; }

.plx-10__sec-visual {
  position: relative;
  aspect-ratio: 4/3;
  border-radius: 12px;
  overflow: hidden;
}
.plx-10__sec-visual-inner {
  position: absolute;
  inset: -15%;
  will-change: transform;
  transform-origin: center;
  border-radius: 16px;
}
.plx-10__section:nth-child(1) .plx-10__sec-visual-inner {
  background: radial-gradient(ellipse at 40% 50%, rgba(80,150,255,0.45) 0%, rgba(7,8,14,0.9) 65%);
}
.plx-10__section:nth-child(2) .plx-10__sec-visual-inner {
  background: radial-gradient(ellipse at 55% 40%, rgba(180,80,255,0.45) 0%, rgba(7,8,14,0.9) 65%);
}
.plx-10__section:nth-child(3) .plx-10__sec-visual-inner {
  background: radial-gradient(ellipse at 50% 60%, rgba(255,130,80,0.45) 0%, rgba(7,8,14,0.9) 65%);
}

/* Hover blur reveal on section visuals */
.plx-10__sec-reveal {
  position: absolute;
  inset: 0;
  background: rgba(7,8,14,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.4s;
}
.plx-10__sec-visual:hover .plx-10__sec-reveal { opacity: 1; }
.plx-10__sec-reveal span {
  font-size: clamp(20px, 3vw, 36px);
  font-weight: 600;
  font-style: italic;
  color: var(--accent);
}

.plx-10__sec-body {}
.plx-10__sec-tag {
  font-family: 'DM Mono', monospace;
  font-size: 9px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--accent);
  opacity: 0.6;
  margin-bottom: 16px;
  display: block;
}
.plx-10__sec-h {
  font-size: clamp(32px, 4.5vw, 60px);
  font-weight: 600;
  line-height: 1.05;
  margin-bottom: 20px;
}
.plx-10__sec-h em { font-style: italic; font-weight: 300; color: rgba(239,242,248,0.7); }
.plx-10__sec-p {
  font-size: 16px;
  font-weight: 300;
  font-style: italic;
  line-height: 1.8;
  color: rgba(239,242,248,0.45);
  max-width: 400px;
}
.plx-10__sec-line {
  width: 48px; height: 1px;
  background: rgba(146,197,240,0.3);
  margin: 20px 0;
}

@media (max-width: 768px) {
  .plx-10__section { grid-template-columns: 1fr; direction: ltr !important; }
  .plx-10__glass-card { padding: 36px 28px; }
}
@media (prefers-reduced-motion: reduce) {
  .plx-10__blobs, .plx-10__blobs-blur, .plx-10__blobs-blur2 { transform: none !important; }
  .plx-10__scroll-line { animation: none; }
  .plx-10__sec-visual-inner { transform: none !important; }
}
(() => {
  const hero     = document.getElementById('plx10-hero');
  const blobs    = document.getElementById('plx10-blobs');
  const blur1    = document.getElementById('plx10-blur1');
  const blur2    = document.getElementById('plx10-blur2');
  const dark     = document.getElementById('plx10-dark');
  const hl       = document.getElementById('plx10-hl');
  const glass    = document.getElementById('plx10-glass');
  const badge    = document.getElementById('plx10-badge');
  const secVisuals = [
    { el: document.getElementById('plx10-v1'), speed: 0.35 },
    { el: document.getElementById('plx10-v2'), speed: 0.3  },
    { el: document.getElementById('plx10-v3'), speed: 0.32 },
  ];

  if (!hero) return;

  let ticking = false;

  function prog(el) {
    const r = el.getBoundingClientRect();
    const h = el.offsetHeight - window.innerHeight;
    return h > 0 ? Math.max(0, Math.min(1, -r.top / h)) : 0;
  }

  function onScroll() {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => {
      const p = prog(hero);
      const vh = window.innerHeight;

      // Blobs parallax downward (bg layer moves slower)
      if (blobs) blobs.style.transform = `translateY(${p * -80}px)`;
      if (blur1) blur1.style.transform  = `translateY(${p * -80}px)`;
      if (blur2) blur2.style.transform  = `translateY(${p * -80}px)`;

      // Cross-fade from sharp → medium blur → heavy blur
      // Phase 1 (0→0.4): sharp visible, blur1 fades in
      // Phase 2 (0.4→0.8): blur1 fades in fully, blur2 starts
      // Phase 3 (0.7→1.0): glass card rises

      const blur1Alpha = Math.max(0, Math.min(1, p / 0.45));
      const blur2Alpha = Math.max(0, Math.min(1, (p - 0.35) / 0.35));
      const darkAlpha  = Math.max(0, Math.min(0.55, (p - 0.4) / 0.4 * 0.55));
      const hlAlpha    = Math.max(0, Math.min(1, 1 - p / 0.5));
      const glassAlpha = Math.max(0, Math.min(1, (p - 0.6) / 0.3));
      const glassY     = (1 - Math.min(1, (p - 0.6) / 0.3)) * 30;

      // Approximate blur in px for badge
      const blurPx = Math.round(blur2Alpha * 28 + blur1Alpha * 14);

      if (blur1) blur1.style.opacity = String(blur1Alpha);
      if (blur2) blur2.style.opacity = String(blur2Alpha);
      if (dark)  dark.style.opacity  = String(darkAlpha);
      if (hl) {
        hl.style.opacity = String(hlAlpha);
        hl.style.transform = `translateY(${p * -20}px)`;
      }
      if (glass) {
        glass.style.opacity = String(glassAlpha);
        glass.style.transform = `translateY(${glassY}px)`;
      }
      if (badge) badge.textContent = `BLUR · ${blurPx}px`;

      // Section visuals parallax
      secVisuals.forEach(({ el, speed }) => {
        if (!el) return;
        const parent = el.closest('.plx-10__sec-visual');
        if (!parent) return;
        const r = parent.getBoundingClientRect();
        if (r.bottom < -100 || r.top > vh + 100) return;
        const centerOffset = (r.top + r.height / 2) - vh / 2;
        el.style.transform = `translateY(${centerOffset * speed * -0.5}px) scale(1.3)`;
      });

      ticking = false;
    });
  }

  window.addEventListener('scroll', onScroll, { passive: true });
  onScroll();
})();

Search CodeFronts

Loading…