<div id="smooth-wrapper">
  <div id="smooth-content">
    <div class="box box-ref">ref</div>
    <div class="box box-a" data-speed="0.5">a</div>
    <div class="box box-b" data-speed="0.5">b</div>
    <div class="box box-c" data-speed="0.5">c</div>
    <div class="refline"></div>
  </div>
</div>
<div class="middle"></div>

<h2>
  ref scrolls normally.<br><br>
  all divs have position of top:80vh.<br><br>
  a, b, and c all have data-speed:0.5<br><br>
  Each object will be at it's "native" placement (with it's top edge aligned to the top of the ref element) when it is centered vertically in the viewport</h2>
:root {
	--dark: #1d1d1d;
  --grey-dark: #414141;
	--light: #fff;
	--mid: #ededed;
  --grey: #989898;
  --gray: #989898;
	--green: #28a92b;
  --green-dark: #4e9815;
	--green-light: #6fb936;
	--blue: #2c7ad2;
	--purple: #8d3dae;
	--red: #c82736;
	--orange: #e77614;
  accent-color: var(--green);
}
body {
  background-color: #111;
  font-family: "Signika Negative", sans-serif, Arial;
  overscroll-behavior: none;
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}


#smooth-content {
  overflow: visible;
  width: 100%;
  /* set a height because the contents are position: absolute, thus natively there's no height */
  height: 4000px;

  background-image:
    linear-gradient(rgba(255,255,255,.07) 2px, transparent 2px),
    linear-gradient(90deg, rgba(255,255,255,.07) 2px, transparent 2px),
    linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
  background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
  background-position: -2px -2px, -2px -2px, -1px -1px, -1px -1px;
}

button {
  position: relative; 
}

.box {
  width: 100px;
  height: 100px;

background: linear-gradient(#61c3fb 50%, #04a8d8 50%);


  position: absolute;
  z-index: 100;
  line-height: 100px;
  font-size: 30px;
  text-align: center;
  
  will-change: transform;
}
.box-ref {
  top: 80vh;
  left : 100px;

}
.box-a {
  top: 80vh;
  left : 250px;
  height:100px;

}

.box-b {
  left : 400px;
  top: 80vh;
  height:200px
}
.box-c {
  width: 100px;
  height: 400px;
  left : 550px;
  top: 80vh;

}

.middle {
  position:fixed;
  top:50%;
  height:2px;
  width:100%;
  background:red;
  transform:translateY(-50%);
}

.refline {
  position:absolute;
  top:80vh;
  background:purple;
  height:2px;
  width:100%;
  z-index:200;
}

h2 {
  margin:0;
  position:fixed;
  color:#eee;
  padding:1em;
  font-weight:normal;
  background:rgb(0, 0, 0, 0.4);
}
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);

// create the smooth scroller FIRST!
let smoother = ScrollSmoother.create({
  smooth: 2,   // seconds it takes to "catch up" to native scroll position
  effects: true // look for data-speed and data-lag attributes on elements and animate accordingly
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://assets.codepen.io/16327/gsap-latest-beta.min.js?r=3.11.2d
  2. https://assets.codepen.io/16327/ScrollSmoother.min.js
  3. https://assets.codepen.io/16327/ScrollTrigger.min.js