JSFiddle - React, Tailwind, and code Playground

by Lachlan Arthur

HTML

<header>
  <h1>Page header</h1>
</header>

<div class="page">
  
  <article style="--image-url: url(https://images.unsplash.com/photo-1549088521-94b6502fec3d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1920&q=80)">
    <div class="content">
      <h4>Panel 1</h4>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eu est quis lacus euismod vestibulum vel nec nulla. Mauris placerat erat sit amet libero lacinia, ut gravida leo commodo.</p>
    </div>
  </article>
  
  <article style="--image-url: url(https://images.unsplash.com/photo-1549072545-a3352136e702?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1920&q=80)">
    <div class="content">
      <h4>Panel 2</h4>
      <p>Suspendisse ut egestas dolor, eu commodo tellus. Sed suscipit vitae arcu id imperdiet. Pellentesque condimentum felis sed accumsan laoreet. Morbi scelerisque sed neque eget imperdiet.</p>
    </div>
  </article>
  
  <article style="--image-url: url(https://images.unsplash.com/photo-1548840775-2061c538a687?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1920&q=80)">
    <div class="content">
      <h4>Panel 3</h4>
      <p>Sed elementum leo dignissim lectus eleifend, in cursus nunc accumsan. Nam tincidunt maximus ipsum, id tincidunt enim placerat ac. Donec a porta est.</p>
    </div>
  </article>
  
</div>

<footer>
  <h1>Page footer</h1>
</footer>

SCSS

:root {
  --scroll-x: 0;
  --scroll-y: 0;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

.page {
  
  counter-increment:
    debugX var( --scroll-x )
    debugY var( --scroll-y );
  
  &::before {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    --hue: calc( var( --scroll-x ) / 10 + var( --scroll-y ) / 10 );
    background-color: hsl( var( --hue ), 100%, 50% );
    content: 'X:' counter( debugX ) ' Y:' counter( debugY );
  }
}

body > header,
body > footer {
  height: 500px;
  text-align: center;
}

article {
  background-image: var( --image-url );
  background-size: cover;
  background-position: calc( var( --scroll-x ) * -1px ) calc( var( --scroll-y ) * -1px );
  height: 500px;
  margin: 0;
  
  .content {
    color: white;
  }
  
  body:not(.no-js) & {
    
    + article {
      position: relative;
    }
    
    + article { top: -500px; }
    + article + article { top: -1000px; }
    + article + article + article { top: -1500px; }
    + article + article + article + article { top: -2000px; }

  }
}

JavaScript

( () => {
	document.body.classList.remove( 'no-js' );

	let lastX = -1;
  let lastY = -1;
  
  const root = document.documentElement;

  
  function update() {
    const { pageXOffset: x, pageYOffset: y } = window;
  
    if ( lastX !== x ) root.style.setProperty('--scroll-x', x);
    if ( lastY !== y ) root.style.setProperty('--scroll-y', y);
    
    lastX = x;
    lastY = y;

  	requestAnimationFrame( update );
  }
  update();
  
  
  // document.addEventListener( 'scroll', update );
} )();