Hero Parallax Grid — Free HTML CSS JS Scroll Snippet

Hero Parallax Grid · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Scroll-to-horizontal
Vertical scroll drives translateX on rows.
Opposite-direction rows
data-dir slides neighbors apart for depth.
Staggered start offsets
Rows begin unaligned for an intentional look.
rAF-throttled scroll
Passive, one transform update per frame.
Overflow-wide rows
max-content rows hold plenty to slide.
Hover lift tiles
Tiles scale up for interactivity.
Themeable tiles
Gradient tiles, swappable for images.
Auto-filled rows
JavaScript generates every tile.

About this UI Snippet

Hero Parallax Grid — Rows Sliding Opposite Ways on Scroll

Screenshot of the Hero Parallax Grid snippet rendered live

The hero parallax grid is the showpiece header where several rows of project tiles slide horizontally as you scroll the page — adjacent rows moving in opposite directions — so the gallery drifts past in a layered parallax. This snippet builds it with plain HTML, CSS, and a lean vanilla JavaScript scroll handler, and it's a favorite opener for studio and portfolio sites.

Scroll drives horizontal movement

Unlike a normal page where scrolling only moves things vertically, here the vertical scroll position is translated into horizontal motion. The update() function reads window.scrollY and, for each row, applies translateX proportional to it: base + scrollY * 0.25 * dir. So as you scroll down, the rows glide sideways. The 0.25 factor controls how fast they slide relative to scroll — smaller is subtler, larger is faster.

Opposite directions per row

Each row carries a data-dir of 1 or -1, and the translate is multiplied by it, so odd rows slide one way and even rows the other. This counter-motion between neighboring rows is the essence of the parallax — your eye reads the opposing layers as depth and energy, whereas rows all sliding the same way would just look like a single moving band. Each row also starts at a different base offset so they aren't aligned at the top edge, which makes the staggering feel intentional.

Efficient scroll handling

The scroll listener is { passive: true } and throttled through requestAnimationFrame with a ticking guard, so the transforms update at most once per frame however fast you scroll. Because the only work per frame is reading scrollY and writing a translateX on three elements, it stays smooth even on long pages. The rows have will-change: transform so the browser promotes them to their own compositor layers.

Overflow-wide rows

Each row is width: max-content and filled with seven tiles in a flex line, so the row is much wider than the viewport and there's plenty of content to slide into view from either side. overflow-x: hidden on the body clips the off-screen tiles so the horizontal motion doesn't create a scrollbar. The tiles themselves lift slightly on hover, so the gallery is interactive as well as animated.

Image-free, themeable tiles

Tiles are gradient rectangles with a label and a bottom scrim, generated in JavaScript from a color palette — so the hero is dependency-free and colorful. Swap each tile's --g background for a real image to turn it into an actual portfolio wall; the parallax logic doesn't care what fills the tiles.

Customizing it

Tune the 0.25 speed factor for more or less drift, add rows (each picks up its direction and a base offset automatically), change the per-row base offsets, adjust tile size, or drop in real images. Combine it with a static headline above, as shown, so the moving grid reads as a backdrop to your title. Pair it with a 3D marquee section or a flip link navigation for a striking studio homepage.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to trace the scroll math by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the data-dir attribute and the per-row base offset combine inside the update function to produce the counter-sliding rows, or why the scroll listener is guarded with a ticking flag before scheduling requestAnimationFrame. The same assistant is useful for optimizing it — ask whether reading window.scrollY and writing three translateX values per frame would still be smooth with many more rows, or if the rows should be virtualized so off-screen tiles are removed from the DOM. It's just as handy for extending the effect: ask it to make the drift speed respond to scroll velocity instead of a fixed multiplier, add a subtle vertical bob to each row on top of the horizontal slide, or lazy-load real project images into the tiles as they enter the viewport. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a horizontally-scrolling parallax gallery hero in plain HTML, CSS, and JavaScript, driven purely by vertical page scroll — no scroll-jacking library, no canvas.

Requirements:
- Several rows stacked vertically, each row set to width: max-content and filled with more tiles than fit in the viewport, with overflow-x: hidden on the page so the extra width never creates a horizontal scrollbar.
- Each row must carry a direction flag (1 or -1) and a distinct starting horizontal offset so rows are not aligned with each other at scroll position zero.
- On every scroll event, compute each row's horizontal translateX as its starting offset plus the current window.scrollY multiplied by a shared speed constant and that row's own direction flag, so adjacent rows visibly slide in opposite directions as the page scrolls, creating a layered parallax.
- The scroll listener must be registered as passive, and must guard against redundant work using a boolean flag combined with requestAnimationFrame so the transform update runs at most once per animation frame regardless of how many scroll events fire.
- Apply will-change: transform to the rows so the browser can promote them to their own compositor layers.
- Generate the tiles programmatically from a small color/gradient palette array (cycling through it) rather than hand-writing each tile in markup, and give each tile a hover state that scales it up slightly.
- Run the transform update once immediately on load, in addition to on scroll, so the layout is correct before the user scrolls at all.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA headline sits above three rows of colorful project tiles.
  2. 2
    Scroll the pageThe rows slide horizontally as you move down.
  3. 3
    Watch the parallaxAdjacent rows drift in opposite directions.
  4. 4
    Hover a tileIt lifts slightly, so the gallery stays interactive.
  5. 5
    Swap in imagesSet each tile's --g to a real project image.
  6. 6
    Tune the speedChange the 0.25 scroll factor for more or less drift.

Real-world uses

Common Use Cases

Studio homepages
Open above a 3D marquee section.
Portfolio heroes
Pair with a flip link navigation.
Agency landing pages
A moving backdrop for an agency hero.
Product showcases
Drift screenshots past a headline.
Photography sites
Slide real shots in parallax rows.
Scroll parallax demos
A reference for scroll-driven horizontal motion.
Related: Independence Day Flag Hoist
See the Independence Day Flag Hoist for a related scroll pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The update function reads window.scrollY and applies translateX to each row as base + scrollY * 0.25 * dir. So scroll position is mapped to horizontal offset rather than vertical, and the rows glide sideways as you scroll the page. The 0.25 factor sets how fast they slide relative to scroll.

Each row has a data-dir of 1 or -1 that multiplies its translate, so adjacent rows slide in opposite directions. The eye reads the opposing layers as depth and motion. Different per-row base offsets also keep the rows from starting aligned, which makes the layering feel deliberate.

No. The rows are width: max-content and wider than the viewport, but overflow-x: hidden on the body clips the off-screen tiles. So tiles slide in and out from the sides without ever adding a horizontal scrollbar to the page.

Yes. The scroll listener is passive and throttled with requestAnimationFrame behind a ticking flag, so it updates at most once per frame. The only per-frame work is reading scrollY and writing a translateX on a few rows, and will-change: transform promotes those rows to their own compositor layers for smooth movement.

Render the rows and tiles from data, then run the scroll handler in a mount effect with cleanup, writing each row's translateX via refs so scrolling doesn't re-render. Keep the ticking flag in a ref. The CSS ports directly. In Tailwind, build the rows with flex and w-max, hide overflow on a wrapper, and apply the computed transforms inline.