<main class="glider-scrollable overflow">
  <div class="fakecontent">Lorem</div>
  <div class="fakecontent">ipsum</div>
  <div class="fakecontent">dolor</div>
  <div class="fakecontent">sit</div>
  <div class="fakecontent">amet,</div>
  <div class="fakecontent">consectetur</div>
  <div class="fakecontent">adipiscing</div>
  <div class="fakecontent">elit</div>
</main>
::-webkit-scrollbar {
  width: 0px;
  height: 0px;
}
html,
body {
  margin: 0;
  border: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
}
.overflow {
  overflow-x: hidden;
  overflow-y: scroll;
  background: #ededed;
  width: 100%;
  height: 100%;
  position: relative;
  -webkit-overflow-scrolling: touch;
}
.thumb {
  width: 68px;
  background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/cat.gif"),
    linear-gradient(
      90deg,
      transparent,
      magenta,
      red,
      yellow,
      limegreen,
      turquoise,
      blue,
      magenta,
      transparent
    );
  background-position: center bottom, center -40px;
  background-repeat: no-repeat, no-repeat;
  background-size: contain, contain;
}

.fakecontent {
  padding: 25vh 0;
  border: 1px solid transparent;
}
(function(scope) {
  var dragging = false;
  var lastY = 0;

  function dragStart(event) {
    dragging = true;
    this.style.pointerEvents = "none";
    this.style.userSelect = "none";

    lastY =
      event.clientY || event.clientY === 0
        ? event.clientY
        : event.touches[0].clientY;
  }

  function dragMove(event) {
    if (!dragging) return;
    var clientY =
      event.clientY || event.clientY === 0
        ? event.clientY
        : event.touches[0].clientY;
    this.scrollTop += (clientY - lastY) / this.thumb.scaling;
    lastY = clientY;
    event.preventDefault();
  }

  function dragEnd(event) {
    dragging = false;
    this.style.pointerEvents = "initial";
    this.style.userSelect = "initial";
  }

  // The point of this function is to update the thumb's geometry to reflect
  // the amount of overflow.
  function updateSize(scrollable) {
    scrollable.style.width = "";
    scrollable.style.width = `calc(${
      getComputedStyle(scrollable).width
    } + 200px)`;

    var thumb = scrollable.thumb;
    var viewport = scrollable.getBoundingClientRect();
    var scrollHeight = scrollable.scrollHeight;
    var maxScrollTop = scrollHeight - viewport.height;
    var thumbHeight = Math.pow(viewport.height, 2) / scrollHeight;
    var maxTopOffset = viewport.height - thumbHeight;

    thumb.scaling = maxTopOffset / maxScrollTop;
    thumb.style.height = `${thumbHeight}px`;

    if (scrollable.isIOS) {
      thumb.nextElementSibling.style.marginTop = `-${thumbHeight}px`;
      var z = 1 - 1 / (1 + thumb.scaling);
      thumb.style.transform = `
        translateZ(${z}px)
        scale(${1 - z})
        translateX(-200px)
      `;
    } else {
      thumb.style.transform = `
         scale(${1 / thumb.scaling})
         matrix3d(
           1, 0, 0, 0,
           0, 1, 0, 0,
           0, 0, 1, 0,
           0, 0, 0, -1
         )
         translateZ(${-2 + 1 - 1 / thumb.scaling}px)
         translateX(-200px)
      `;
    }
  }

  function makeCustomScrollbar(scrollable) {
    // Edge requires a transform on the document body and a fixed position element
    // in order for it to properly render the parallax effect as you scroll.
    // See https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/5084491/
    if (getComputedStyle(document.body).transform == "none")
      document.body.style.transform = "translateZ(0)";
    var fixedPos = document.createElement("div");
    fixedPos.style.position = "fixed";
    fixedPos.style.top = "0";
    fixedPos.style.width = "1px";
    fixedPos.style.height = "1px";
    fixedPos.style.zIndex = 1;
    document.body.insertBefore(fixedPos, document.body.firstChild);

    scrollable.style.perspectiveOrigin = "top right";
    scrollable.style.transformStyle = "preserve-3d";
    scrollable.style.perspective = "1px";

    var perspectiveCtr = document.createElement("div");
    perspectiveCtr.style.perspectiveOrigin = "top right";
    perspectiveCtr.style.transformStyle = "preserve-3d";
    perspectiveCtr.style.width = "100%";

    perspectiveCtr.style.position = "absolute";
    perspectiveCtr.style.pointerEvents = "none";
    perspectiveCtr.classList.add("perspective-ctr");

    while (scrollable.firstChild)
      perspectiveCtr.appendChild(scrollable.firstChild);

    scrollable.insertBefore(perspectiveCtr, scrollable.firstChild);
    var thumb = document.createElement("div");
    thumb.classList.add("thumb");
    thumb.style.pointerEvents = "initial";
    thumb.style.position = "absolute";
    thumb.style.transformOrigin = "top right";
    thumb.style.top = "0";
    thumb.style.right = "0";
    perspectiveCtr.insertBefore(thumb, perspectiveCtr.firstChild);
    scrollable.thumb = thumb;
    scrollable.perspectiveCtr = perspectiveCtr;

    // We are on Safari, where we need to use the sticky trick!
    if (getComputedStyle(scrollable).webkitOverflowScrolling) {
      scrollable.isIOS = true;
      thumb.style.right = "";
      thumb.style.left = "100%";
      thumb.style.position = "-webkit-sticky";
      perspectiveCtr.style.perspective = "1px";
      perspectiveCtr.style.height = "";
      perspectiveCtr.style.width = "";
      perspectiveCtr.style.position = "";
      Array.from(scrollable.children)
        .filter(function(e) {
          return e !== perspectiveCtr;
        })
        .forEach(function(e) {
          perspectiveCtr.appendChild(e);
        });
    }

    scrollable.thumb.addEventListener("mousedown", dragStart.bind(scrollable), {
      passive: true
    });
    window.addEventListener("mousemove", dragMove.bind(scrollable));
    window.addEventListener("mouseup", dragEnd.bind(scrollable), {
      passive: true
    });
    scrollable.thumb.addEventListener(
      "touchstart",
      dragStart.bind(scrollable),
      { passive: true }
    );
    window.addEventListener("touchmove", dragMove.bind(scrollable));
    window.addEventListener("touchend", dragEnd.bind(scrollable), {
      passive: true
    });

    var f = function() {
      updateSize(scrollable);
    };
    requestAnimationFrame(f);
    window.addEventListener("resize", f);
    return f;
  }

  window.addEventListener("load", function() {
    Array.from(document.querySelectorAll(".overflow")).forEach(scrollable => {
      makeCustomScrollbar(scrollable);
      updateSize(scrollable);
    });
  });
})(self);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.