Horizontal pin

Scrolling down slides a row of panels sideways, then lets the page carry on vertically.

How it works

A sticky container is placed inside a section as tall as the track is wide. While the container is pinned, vertical progress through the section is mapped to translateX on the track, from 0 to the track’s overflow width.

Full file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Horizontal pin</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-font-smoothing: antialiased; }

body { background: #fdfcdc; color: #073b4c; }
.intro { height: 70vh; display: grid; place-content: center; text-align: center; padding: 0 6vw; }
.intro h1 { font-size: clamp(2rem, 6vw, 3.5rem); margin: 0; }
.sticky { position: sticky; top: 0; height: 100vh; overflow: hidden; display: flex; align-items: center; }
.track { display: flex; gap: 4vw; padding: 0 8vw; will-change: transform; }
.track article {
  flex: 0 0 70vw; max-width: 640px; height: 70vh; border-radius: 24px; background: var(--c);
  display: flex; align-items: flex-end; padding: 28px; color: #fff;
  font: 800 clamp(2.5rem, 7vw, 5rem)/1 system-ui, sans-serif;
}
</style>
</head>
<body>
<section class="intro"><h1>Scroll down to go sideways</h1></section>
<section class="pin" id="pin">
  <div class="sticky">
    <div class="track" id="track">
      <article style="--c:#ef476f">One</article>
      <article style="--c:#ffd166">Two</article>
      <article style="--c:#06d6a0">Three</article>
      <article style="--c:#118ab2">Four</article>
      <article style="--c:#073b4c">Five</article>
      <article style="--c:#9b5de5">Six</article>
    </div>
  </div>
</section>
<section class="intro"><h1>And down again</h1></section>
<script>
const pin = document.getElementById('pin');
const track = document.getElementById('track');
function size() {
  pin.style.height = (track.scrollWidth - innerWidth + innerHeight) + 'px';
  update();
}
function update() {
  const r = pin.getBoundingClientRect();
  const dist = track.scrollWidth - innerWidth;
  const p = Math.min(1, Math.max(0, -r.top / (r.height - innerHeight)));
  track.style.transform = 'translate3d(' + (-p * dist) + 'px,0,0)';
}
addEventListener('scroll', update, { passive: true });
addEventListener('resize', size);
size();
</script>
</body>
</html>

More horizontal effects