022

Horizontal Scroll Section

£10 or the whole Vault, £99

A pinned section where vertical scroll pans a row of full-viewport panels sideways, with snap to panel edges, per-element parallax and a progress readout driven by the same timeline.

ScrollTrigger Lenis intermediate
5 more details
horizontal-scrollpinningscrubparallaxsnap

This demo reads better at your own screen size than in the frame below:

Updated

About this effect

A cinematic horizontal scroll section built on ScrollTrigger pinning. The section locks to the viewport while vertical input carries visitors through full-screen editorial chapters, then releases cleanly at the final panel. Panels can use any mix of widths; travel comes from the track's real width, so the sequence stays accurate through resizes, font loads and content changes. Optional panel-edge snap, per-element parallax, progress bar, and chapter readout are driven by data attributes.

Read the full effect overview

The demo places the rail inside an ordinary vertical page (an opening spread above it and a closing section below) because the turn from vertical to horizontal only reads as a turn if there is vertical scrolling on either side of it.

Horizontal Scroll Section - GSAP animation effect preview

What's included

8 items
  • Pinned horizontal scrolling driven by a single scrubbed x-position tween
  • Variable-width panels: distance measured from scrollWidth, not panel counts
  • Snap-to-panel edges with measured snap points that survive resizes
  • Per-element parallax via containerAnimation ScrollTriggers
  • Scroll progress bar plus nearest-panel readout updated without tween churn
  • Fully responsive: functional end values plus invalidateOnRefresh
  • Configurable scrub smoothing and traverse speed via data attributes
  • Accessible: keyboard focus scrolls panels into view, reduced motion stacks panels vertically

Perfect for

5 use cases
  • Portfolio and case study galleries with a cinematic sideways flow
  • Product feature walkthroughs that step through panels one by one
  • Storytelling chapters and timeline sequences on landing pages
  • Service or process breakdowns with numbered stages
  • Award-style agency sites that need a memorable scroll moment

How it works

1 section

ScrollTrigger pins the section and scrubs the track sideways by exactly the distance it overflows the viewport. Both the travel and the trigger's end point are recalculated on every resize rather than cached, so a rotation or a late font load cannot leave the rail short or long. Snap points come from each panel's own position on refresh. Parallax layers are tied to the main tween so they track the horizontal motion rather than the page scroll, and the progress bar is written once per frame.

Plugins ScrollTrigger
Difficulty Intermediate
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Horizontal Scroll Section opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£10 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before closing </body> tag:

Code snippet omitted: it ships with the download.

3. Add the effect HTML anywhere in your <body>:

Code snippet omitted: it ships with the download.

That's it. The section pins when it reaches the top of the viewport and the track translates left as the user scrolls down, releasing when the last panel arrives.

Using It With Your Own Design

Put ordinary page above and below it. The effect needs nothing else to work, but a rail that starts at the top of the document is just a horizontal page: the visitor never feels it turn. Give it a section of normal vertical scrolling before the pin and more after it, and the hand-off from down to sideways becomes the moment people remember. The demo does this with an opening spread and a closing aftercare section; both are plain markup with no effect attributes on them at all.

Two details worth copying from the demo:

  • The section above is just under one viewport tall (min-height: 82svh), so the top edge of the first panel shows below the fold. The page announces the rail before you reach it.
  • Its reveals are CSS-only (animation-timeline: view() for the closing section), so nothing competes with ScrollTrigger for the scroll, and both end on the visible state — an unsupported browser or reduced motion lands on a readable page rather than a blank one.

What the effect requires of your markup: a [data-hscroll] section containing a .hscroll-track with at least two .hscroll-panel children. Panel widths are free; travel is measured from the track's real scrollWidth.

What is only the demo's CSS: everything named .stage*, .plate*, .rail-cue*, .rail-progress*, .page-intro, .page-outro, .rule-row*, .intro-* and .outro-*. The effect never reads any of them.

The non-obvious CSS the effect depends on:

  • .hscroll { height: 100svh; overflow: hidden } — the pin measures this, and the overflow is what hides the rest of the track.
  • .hscroll-track { display: flex } with panels that do not shrink (flex: 0 0 …), or scrollWidth collapses to the section width and the track has nowhere to travel.
  • The .no-js and @media (prefers-reduced-motion: reduce) blocks that unpin the section and stack the panels vertically. Keep both halves: they are the only thing standing between JavaScript being unavailable, or motion being turned down, and a page with four fifths of its content parked off-screen.

Options

All options are data attributes on the [data-hscroll] element unless noted.

Attribute Values Default Description
data-hscroll - required Marks the section to pin and scroll horizontally
data-hscroll-scrub number, true 1 Scrub smoothing in seconds. true locks the track 1:1 to scroll with no smoothing
data-hscroll-snap true, false false Snap the track to the nearest panel's left edge when scrolling stops
data-hscroll-speed number 1 Traverse speed multiplier. 2 crosses the track in half the scroll distance, 0.5 doubles it
data-hscroll-parallax number 0.2 (when present) On any element inside a panel: drifts it against the track direction as its panel crosses the viewport. Higher = faster drift
data-hscroll-progress - optional On an element inside the section: its scaleX is driven from 0 to 1 as the track traverses
data-hscroll-readout - optional Displays the nearest panel number as a zero-padded value (01, 02, etc.)
data-hscroll-total - optional Displays the total panel count as a zero-padded value

Responsive Behavior

The tween's x value and the trigger's end are both functions, and the trigger uses invalidateOnRefresh: true. Every ScrollTrigger.refresh() (window resize, orientation change, font load) re-measures scrollWidth, the pin distance, and the snap points. No manual resize handling is needed.

Initialization also waits for document.fonts.ready so panel widths are measured with the final fonts.

Accessibility

  • Reduced motion: with prefers-reduced-motion: reduce, the section is not pinned; panels stack vertically as normal page content. This is enforced twice: a CSS @media fallback (works even without JavaScript) and a gsap.matchMedia() branch that adds .hscroll--static
  • Keyboard navigation: focusable content inside panels stays reachable. A focusin listener on the track converts the focused panel's position into the matching page scroll position, so tabbing into an off-screen panel scrolls the pinned section to reveal it (using lenis.scrollTo when Lenis is present)
  • Focus visible: :focus-visible styles on buttons match :hover
  • Semantics: give the section an aria-label; decorative parallax numbers use aria-hidden="true"

Reduced Motion Behavior

When prefers-reduced-motion: reduce is set:

  • No pinning, no horizontal translation, no parallax
  • Panels display stacked vertically at full width
  • The progress bar is hidden

Lenis Smooth Scroll

Lenis is optional and auto-detected. When ScrollTrigger is present, Lenis and ScrollTrigger share GSAP's ticker (autoRaf: false) to keep pinned scrubbing synchronized; Lenis is also used for keyboard focus scrolling. The effect works with native scrolling when the Lenis script tag is omitted.

Code snippet omitted: it ships with the download.

Browser Support

Modern browsers (ES6+). Not compatible with IE11.

Dependencies

Required:

  • GSAP 3.12+
  • ScrollTrigger plugin

Optional:

  • Lenis (smooth scroll integration; effect works without it)

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.