Parallax
A gradient background sliding from +60 to −60 px over the 520 px travel of a 280 px box (animation-timeline: scroll()), and three backdrop-filter cards that rise 30 px and go from 0 to 1 opacity during their first 33 px inside the box (view(), entry → entry 50%). Two CSS timelines, zero JavaScript, 3.1 KB.
Updated
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Scroll has 70 effects, including 12 free. Explore the category →
3 usage examples



How it works
The sold HTML: the .demo-preview frame (centered flex, min-height: 300px, #0a0a0f background, white text, sans-serif), a .sdc-scroll-container box of 100% × 280px with overflow-y: auto and scroll-behavior: smooth, an 800 px stage .sb-parallax-content in position: relative; overflow: clip, and two layers inside it: .sb-parallax-bg, absolute with inset: -60px 0 (920 px), the 135° gradient #6366f1 → #d946ef and the word PARALLAX centered (32 px, weight 800); .sb-parallax-items (z-index: 2, padding: 200px 20px 20px, flex column, gap: 100px) with three .sb-parallax-item — padding: 16px, 12 px radius, rgba(0,0,0,.3), backdrop-filter: blur(8px), a 13.6 px h4, an 11.5 px p in white at 70%. Measured: 66 px cards at 200, 366 and 532 px from the top of the stage, travel 800 − 280 = 520 px. The hint .sdc-scroll-hint “Scrollez ↓” (absolute, 8 px from the bottom of the frame, 10.4 px, white at 30%) pulses between .3 and .7 every 2 s. Empty JS field, no site variable, no external resource, no console error.
Two animations, two clocks. The background carries animation-name: sbParallaxBg (translateY(60px) → translateY(-60px), linear) with animation-timeline: scroll(): its progress is the scroll position of the nearest scroll container, here the 280 px box, and animation-range: normal spreads the 120 px over the whole travel. Measured in 130 px steps in Chromium 151: 60, 30, 0, −30, −60 px; the layer's 60 px overhang keeps the box covered from end to end. Each card carries animation-name: sbFadeUp (to opacity: 1; transform: translateY(0)) with animation-timeline: view() and animation-range: entry entry 50%: the clock is the card's passage through the box's window, from “its top edge touches the bottom of the box” to “half of it is in”, i.e. 33 px of scrolling. animation-fill-mode: both holds it at opacity: 0; translateY(30px) before the range and at 1 / 0 after. Measured: card 1 is already in at rest (its range falls between −80 and −47 px), card 2 reveals between 86 and 119 px (opacity 0 at 0, 1 at 130), card 3 between 252 and 285 px (0.24 and translateY(22.7px) at 260, 1 at 390).
The line that holds it all together is overflow: clip on the stage. With overflow: hidden, the stage itself becomes a scroll container — with no travel, its 800 px being full — and that is the one scroll() and view() pick: measured, inactive timelines, transform: none on the background from 0 to 520 px and cards frozen at opacity 1 from rest. clip crops the same way without creating a container: both clocks climb up to the box. The direction comes from the keyframes: the background travels 120 px further up than the cards over the course, so it runs 1.23 times faster than they do — the opposite of classic parallax, where the background lags. The word PARALLAX crosses the box between 115 and 405 px of scrolling and passes behind card 2 around 260 px, blurred by the backdrop-filter. No id, no script: several copies coexist on one page, each tied to its box.
Accessibility
- prefers-reduced-motion not in the code: measured under emulation in Chromium, nothing changes (background 60 → −60 px, cards fading in, hint
running,scroll-behavior: smooth). A background moving at a different speed from the text is the motion WCAG 2.3.3 targets. Add@media (prefers-reduced-motion: reduce) { .sb-parallax-bg, .sb-parallax-item { animation: none } .sb-parallax-item { opacity: 1; transform: none } .sdc-scroll-hint { animation: none } .sdc-scroll-container { scroll-behavior: auto } }. Nothing is hidden for all that: a card is at opacity 0 only below the bottom edge of the box and during its first 33 px of entry. - Keyboard: measured in Chromium 151, Tab focuses the box without a
tabindex, Arrow Down moves 40 px (background at 50.8 px), Page Down 285 px (all three cards revealed), End goes to 520; Firefox 153 also focuses the box on Tab. For Safari, addtabindex="0", a visible:focus-visibleand anaria-label. With a finger, the gesture must start inside the box;overscroll-behavior: containkeeps the page from being dragged along at the end of the travel. - Contrast (computed at both ends of the gradient, card = 30% black on top): white 13.6 px headings at 7.7:1 on the blue and 6.3:1 on the pink; paragraph in white at 70% (11.5 px) at 4.7:1 on the blue but 3.9:1 on the pink, below 4.5:1 — raise it to 85% (5.0:1) or darken the card to
rgba(0,0,0,.45)(5.1:1). The word PARALLAX (32 px, weight 800, 3:1 threshold) measures 4.5:1 on the blue, 3.5:1 on the pink. The hint at 30% swings between 1.2:1 and 1.8:1: decorative, setaria-hidden="true". - Screen readers and structure: three
divs with oneh4each — a level 4 with no h2/h3 above breaks the page outline, use the level that follows your current heading. Opacity 0 removes nothing from the accessibility tree: all three cards are read whatever the scroll position. The word PARALLAX is read as is (decorative:aria-hidden="true") and “Scrollez ↓” is hard-coded in French. Catalog leftover: an inlinestyle="font-family: Inter, …"on eachh4. No JavaScript, so nothing changes without it; withoutanimation-timeline, everything shows flat (see compatibility).
Browser compatibility
CSS only: animation-timeline: scroll() and view(), animation-range (Scroll-driven Animations Level 1), overflow: clip, backdrop-filter, inset, a gradient, @keyframes. Zero JavaScript, zero dependencies. Scroll timelines exist in Chrome and Edge 115+, Safari and iOS 26 (measured in WebKit 26.5: 60 → 0 → −60 px, card 3 at 0.24 at 260 px, same as Chromium); Firefox: no version. Measured in Firefox 153: CSS.supports('animation-timeline: scroll()') returns false, the declaration is dropped and both animations fall back to 0 s — the background stays at transform: none (fill-mode: none), the cards take their last keyframe (opacity: 1, translateY(0), fill-mode: both): everything is visible, flat, the box scrolls normally, the hint pulses. No @supports fallback in the code: nothing to catch, the final state of the keyframes is the readable state.
Without animation-timeline (Firefox, Safari and iOS before 26, Chrome before 115): still background, cards at opacity 1 from load, ordinary scrolling — only the motion is missing. Without overflow: clip (Safari before 16): the layer overhangs the stage by 60 px at the bottom, the box scrolls 580 px instead of 520, nothing else. Without backdrop-filter: 30% black panels with no blur, text readable.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sb-parallax-content">
<div class="sb-parallax-bg">
<div class="sb-parallax-text">…</div>
</div>
<div class="sb-parallax-items">
<div class="sb-parallax-item">
<h4>…</h4>
<p>…</p>
</div>
<div class="sb-parallax-item">
<h4>…</h4>
<p>…</p>
</div>
<div class="sb-parallax-item">
<h4>…</h4>
<p>…</p>
</div>
</div>
</div>
</div>
<span class="sdc-scroll-hint">…</span>
</div>
The full HTML + CSS + JS of Parallax, ready to paste — plus all 811 effects. Or €299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Background amplitude (CSS — @keyframes sbParallaxBg and .sb-parallax-bg { inset: -60px 0 }) |
60 → −60 px (120 px over a 520 px travel) | The layer's overhang follows the amplitude: ±100 px require inset: -100px 0, otherwise the frame's background shows as a 40 px band at the top at rest and at the bottom at the end of the travel. At 0 → 0, only the cards animate. |
Parallax direction (CSS — @keyframes sbParallaxBg) |
the background runs 1.23 times faster than the cards | Swap the two values (translateY(-60px) → translateY(60px)) so the background lags behind the content (0.77 times its speed), the classic reading of depth; the 60 px overhang then serves the other way. |
Travel (CSS — .sb-parallax-content { height: 800px }, .sdc-scroll-container { height: 280px }) |
520 px | animation-range: normal spreads the background's 120 px over the whole travel: a 1,500 px stage (1,220 of travel) slows the background accordingly. The cards are set by their position (padding-top: 200px, gap: 100px): less than 280 px from the top, a card is visible at rest, like the first one. |
Reveal range (CSS — .sb-parallax-item { animation-range: entry entry 50% }) |
entry → entry 50% (33 px for a 66 px card) | entry 0% entry 100% spreads the fade over the card's full height; entry 20% entry 80% lets it cross the edge before starting; cover 0% cover 30% anchors the end at 30% of its passage. Since the range is a percentage of the card, a taller card lengthens it accordingly. |
Cards' starting state (CSS — .sb-parallax-item { opacity: 0; transform: translateY(30px) }) |
30 px lower, invisible | The sbFadeUp keyframe only has a to: the start is the card's base style. translateY(60px) scale(.96) for a stronger entrance, opacity: .4 for a card readable before its entry, or remove the transform for a pure fade. |
Card material (CSS — background: rgba(0,0,0,.3), backdrop-filter: blur(8px)) |
black at 30%, 8 px blur | The blur reads whatever passes behind (the gradient, the word PARALLAX): on a photo, blur(14px); for a light theme, rgba(255,255,255,.18) with dark text. Darkening to .45 brings the 70% paragraph above 4.5:1 across the whole gradient. |
Gradient and word (CSS — .sb-parallax-bg { background }, .sb-parallax-text) |
135° #6366f1 → #d946ef, PARALLAX 32 px | Everything set on .sb-parallax-bg slides with it, multiple backgrounds included (lines as repeating-linear-gradient, a radial glow): the most visible way to make the parallax read. The word becomes a watermark by lowering its color (rgba(255,255,255,.15)); give it aria-hidden="true". |
Hint and easing (CSS — .sdc-scroll-hint, .sdc-scroll-container { scroll-behavior: smooth }) |
“Scrollez ↓” pulsing 2 s, smooth scrolling | The hint is a catalog leftover: hard-coded French, absolute relative to .demo-preview (without that frame it drops to the bottom of the page) — delete the span, the sdcHintPulse keyframe goes with it. smooth only eases programmatic scrolls (anchors, scrollTo), not the wheel. |
FAQ
animation-timeline in no version, Chrome since 115, Safari since 26 — elsewhere the effect shows flat, still background and visible cards. The stage: if .sb-parallax-content goes back to overflow: hidden, or if an element with overflow: hidden or auto slips in between the layers and the box, that full block becomes the container scroll() and view() pick, with no travel: measured, transform: none and cards frozen at opacity 1. Keep overflow: clip. The box, finally, must stay shorter than its content, otherwise nothing scrolls. A wrapper in overflow: hidden around the frame does no harm: the box is nearer.overflow-y) and the .demo-preview frame: the nearest container becomes the page, scroll() follows its full height and view() the browser window — the cards reveal as they enter from the bottom of the screen. Two precautions: an ancestor in overflow: hidden (a horizontal-overflow-proof .wrapper) would capture both timelines — switch it to overflow: clip, or write animation-timeline: scroll(root) for the background; and the background layer, absolute inside an 800 px stage, must take the height of the section it dresses, or go position: fixed.scroll() with their own keyframes — a glow in scale(1) → scale(1.3), shapes and rings rising 60 to 100 px while rotating a few degrees, dots, a title going from .7 to 1 opacity. Five speeds, no view(), nothing to read: its layers are decorative, the title is its only text. fx-0538 has a single background layer but carries a content column: three backdrop-filter cards, each revealed by view() as it enters the box, independently of the background. fx-0592 for a section backdrop; fx-0538 as soon as cards — rates, plans, instructions — must stay readable over a moving background.