A horizontal scroll section pins to the viewport while normal vertical scrolling slides a row of panels sideways, then releases the page when the last panel arrives. It’s a staple of portfolio and product sites, and ScrollTrigger handles all the hard parts. Here’s a working basic version.

What you’ll build

A full-height section containing a horizontal track of panels. When the section reaches the top of the viewport it pins, scroll input moves the track sideways, and the pin releases once the track has fully traversed. The travel distance is measured from the track’s real width, so it works with any number of panels and respects reduced motion preferences.

The full Horizontal Scroll Section effect; this tutorial builds a simpler version. Scroll inside the frame to drive it. Get the full code →

Step 1: Add GSAP

Add GSAP and ScrollTrigger before your closing </body> tag:

<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>

Step 2: Add the markup and CSS

The structure is a section wrapping a flex track of panels:

<section class="hscroll">
  <div class="hscroll-track">
    <div class="hscroll-panel">Panel one</div>
    <div class="hscroll-panel">Panel two</div>
    <div class="hscroll-panel">Panel three</div>
    <div class="hscroll-panel">Panel four</div>
  </div>
</section>
.hscroll {
  overflow: hidden;
}

.hscroll-track {
  display: flex;
  height: 100vh;
}

.hscroll-panel {
  flex: 0 0 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Reduced motion: stack the panels as a normal vertical page */
@media (prefers-reduced-motion: reduce) {
  .hscroll-track {
    flex-direction: column;
    height: auto;
  }

  .hscroll-panel {
    flex-basis: auto;
    min-height: 60vh;
  }
}

Panels don’t have to be 100vw. Any mix of widths works because the JavaScript measures the track rather than counting panels.

Step 3: Add the effect code

document.addEventListener('DOMContentLoaded', function () {
  // Reduced motion: the CSS fallback stacks panels vertically instead
  if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;

  gsap.registerPlugin(ScrollTrigger);

  const track = document.querySelector('.hscroll-track');

  const distance = function () {
    return track.scrollWidth - window.innerWidth;
  };

  gsap.to(track, {
    x: function () { return -distance(); },
    ease: 'none',
    scrollTrigger: {
      trigger: '.hscroll',
      start: 'top top',
      end: function () { return '+=' + distance(); },
      pin: true,
      scrub: 1,
      invalidateOnRefresh: true
    }
  });
});

Three details matter here. The tween’s x and the trigger’s end are functions, so combined with invalidateOnRefresh every resize re-measures the layout instead of animating to a stale distance. The end of '+=' + distance() means one pixel of vertical scroll equals one pixel of horizontal travel, which feels natural. And ease: 'none' keeps the track locked to the scrollbar; easing belongs on elements inside the panels, not on the scrub itself.

Optional: snap to panels

If every panel is the same width, snapping is one line in the scrollTrigger config:

snap: 1 / (track.children.length - 1)

That’s it

Scroll down and the section pins while the panels slide past. This basic version handles any number of equal-width panels, re-measures on resize, and falls back to a vertical layout for reduced motion users.

Put the section in context

A horizontal section works best when each panel makes one clear point. Try a project image, a short title and a link to the full case study. Keep long descriptions in the normal page flow, and check that keyboard users can reach every link and see the panel containing it.

The Horizontal Scroll Template builds a whole page around a sideways track. Use its live preview to judge the transition into and out of the section, then compare it with the other portfolio website templates if your page needs a different structure.

To practise the pinning and scrubbing decisions behind this technique, the Scroll animation for developers course has interactive labs for trigger positions, scroll progress and pinned chapters. It teaches those shared foundations; horizontal tracks and snapping are outside the course’s scope.


Want more control?

The Horizontal Scroll Section effect includes features this basic version doesn’t have:

  • Variable-width panels - mixed layouts that snap correctly and survive resizes
  • Per-element parallax - headline and image layers drift at their own speeds
  • Progress bar - follow your position through the section
  • Speed control - traverse the track faster or slower than the basic version
  • Keyboard accessibility - focusing a link inside an off-screen panel scrolls it into view

View the full effect with all options →

Browse the GSAP scroll animations collection for other ways to guide a reader through a page, or the full list of GSAP animation examples for everything else in the library on one page.