Perspective Scroll
Four colored panels ("Layer 1" to "Layer 4") are born 1,200 px deep and rush toward the screen until they fly past it in 8 s, one every 2 s, each with its own offset: a tunnel of panels in pure CSS (perspective, translateZ, depth sorting), with no scrolling and no JavaScript despite the name, laid out as a still staircase when the user reduces motion.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →
3 usage examples



How it works
The root .fx-0024-root (#0a0a0f background, at least 300 px tall, overflow: hidden) holds the .perspective-scroll-container (full width × 250 px, perspective: 500px) and four .perspective-plane elements placed at the center: indigo 120 × 60 px, pink 100 × 50, green 140 × 70 and amber 90 × 45, all 70% opaque. A single animation, fx0024Fly, linear over 8 s, moves them from translateZ(-1200px) to translateZ(200px); opacity goes from 0 to 1 over the first 10 percent, stays at 1 until 80%, then drops back to 0. With a 500 px perspective, a panel shows at 29% of its size at the start (500 / 1,700) and 167% at the end (500 / 300): it grows almost six times during its flight, then fades before passing "behind" the viewer.
Each panel carries two variables, --fx0024-x and --fx0024-y (−50% / −50% = centered; then −80% / −80%, −20% / −30%, −70% / −20%), used in both steps of the keyframes: the offset holds for the whole flight, and since the perspective enlarges it along with the panel, the four spread away from the center as they approach, like the walls of a tunnel. Delays are negative (0, −6, −4 and −2 s): as soon as the page loads, the four panels are already in flight at four different depths, 2 s apart, and a new panel arrives every 2 s. Measured in Chromium 151 and Firefox 153: 5.4 to 5.8% of the scene's pixels change over 0.8 s, and both read the variables inside the keyframes.
The container has transform-style: preserve-3d: panels are sorted by depth, and the one coming close passes in front of the one leaving the back. That is why the container has no overflow and the root does the clipping: measured in Chromium and Firefox, overflow: hidden and even overflow: clip on the container flatten the scene, and the panels are then painted in HTML order, a distant panel on top of a near one. Not a single line of JavaScript: the browser animates four transform and four opacity values. The catalog name says "Scroll", but nothing listens to scrolling: the loop runs on its own, wherever you place the effect.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, a block scoped to.fx-0024-rootstops the animation and lays the four panels out as a staircase, from −600 px (top left) to 0 px (bottom right): a still image of the tunnel where every label stays readable. Measured: 0% of pixels change over 0.8 s in Chromium, Firefox and WebKit (5.4 to 5.8% without the preference). This setting matters more here than elsewhere: content rushing toward the viewer is the kind of motion that bothers people prone to dizziness. - Text: the labels are real text, read by screen readers in HTML order whatever the panel's position. On screen, each one is readable for only a few seconds per loop: do not give the panels information that exists nowhere else, as the scenes above show (the list of dates or names stays in the page).
- Contrast: white text on 70% panels over
#0a0a0f, calculated at 7.3:1 on indigo, 6.2:1 on pink, 4.6:1 on green and 4.0:1 on amber. Amber stays under the 4.5:1 threshold for small text: darken it or switch its text to black if the label must be read. During the fade-in and fade-out, contrast drops with opacity. - Pace: one continuous 8 s loop, with no flashing and no sudden change in brightness; no interaction, nothing to reach with the keyboard.
Browser compatibility
CSS only: perspective, transform-style: preserve-3d, translateZ and CSS variables read inside @keyframes, zero JavaScript, no dependencies. Measured in Chromium 151 and Firefox 153: same flight, same depth sorting, same stop under reduced motion; two copies of the effect on one page work (no ids). Playwright's WebKit (26.5) flattens all CSS 3D, even a reference cube: rendering in Safari has not been verified on a real Safari.
If a rule on your page adds overflow, filter or an opacity below 1 to the container, the scene is flattened (measured in Chromium and Firefox for all three): the panels still fly, but stack in HTML order instead of being sorted by depth. The labels remain text in the page, even when the animation is not running.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0024-root">
<div class="perspective-scroll-container">
<div class="perspective-plane plane-1">Layer 1</div>
<div class="perspective-plane plane-2">Layer 2</div>
<div class="perspective-plane plane-3">Layer 3</div>
<div class="perspective-plane plane-4">Layer 4</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Speed (CSS — 8s in the four animation declarations) |
8 s, delays 0 / −6 / −4 / −2 s | Change the four durations together and keep delays equal to 0, −¾, −½ and −¼ of the duration: at 12 s, use 0 / −9 / −6 / −3 s. A panel then arrives every 3 s instead of 2. |
Flight depth (CSS — translateZ(-1200px) and translateZ(200px) in the keyframes) |
−1,200 px → +200 px | A more distant start makes the panel smaller at first (−2,000 px: 20% of its size). The end must stay below the perspective value (500 px): beyond it, the panel is behind the viewer and disappears. |
Perspective (CSS — .perspective-scroll-container) |
500px | Smaller, the growth is more abrupt: at 300 px, a panel goes from 20% to 300% of its size. Larger (900 px), the flight becomes a gentle glide. If you lower it below 200 px, lower the end of the flight too. |
Offsets (CSS — --fx0024-x and --fx0024-y on each .plane-N) |
−50 / −50%, −80 / −80%, −20 / −30%, −70 / −20% | As a percentage of the panel's size; −50% = centered. The further from −50%, the more the panel moves away from the center as it approaches: from −110% to +10%, the tunnel opens clearly; positive values (+40%) send every panel to the same side, like roadside signs. |
Panels (HTML and CSS — .perspective-plane, .plane-1 to .plane-4) |
4 panels, 90 to 140 px wide, 70% colors | Put any text or image in a panel. For a fifth panel, copy a .plane-N rule and spread the delays over the duration (8 s / 5 = one delay every −1.6 s). Larger panels (200 px and up) make the labels readable earlier in the flight. |
Background and frame (CSS — .fx-0024-root) |
#0a0a0f, at least 300 px tall | Change or remove the root's background to set the tunnel on your own backdrop. Keep its overflow: hidden: it is what clips the enlarged panels, since the container must not have one. |
FAQ
overflow: hidden, and even with overflow: clip, on the element that has preserve-3d, the panels are no longer sorted by depth and are painted in HTML order: a distant panel passes over a near one. Clipping is therefore left to the root .fx-0024-root, one level up. filter and an opacity below 1 on the container have the same effect (also measured).