← Back to gallery
CSS

Fixed Background Parallax Baseline

Foreground scrolls past a fixed background image via background-attachment: fixed. Three motion shapes — Linear (steady), Eased (calmer editorial pace), and Steps (snap card-to-card) — each with explicit mobile + reduced-motion fallbacks since iOS Safari ignores fixed-attachment.

parallaxbackground-attachmentfixed-backgroundduplicated-stackios-fallbackprefers-reduced-motion

Background-attachment baseline · scrolling foreground

Fixed Background Parallax Baseline

Three motion archetypes for the fixed-background parallax pattern — Continuous Linear (steady hero ribbon), Eased Drift (calm editorial pace with ease-in-out timing), and Snap Steps (digital-ticker stepping). The hero scene NEVER moves; only the foreground card stack scrolls over it via translateY, mirroring the gallery thumbnail composition exactly.

Linear scroll · thumbnail baseline

Continuous Linear

A steady linear scroll over a fixed hero scene. The translateY animation runs from 0 to -50% on a duplicated stack so the loop seam is invisible. Identical to the gallery thumbnail; this is the canonical baseline.

  • translateY
  • linear
  • duplicated stack

Ease-in-out · calm editorial

Eased Drift

A calmer scroll on a longer cycle with ease-in-out timing — the stack accelerates between loop seams and decelerates at the end. Suits long-form editorial pages where the parallax should support reading rhythm, not pull attention.

  • ease-in-out
  • long cycle
  • editorial

Stepped · ticker stop

Snap Steps

A stepped scroll where the stack snaps from card to card with no in-between motion — dashboard-ticker / changelog feel. Each step lands exactly one card down so the layout reads as discrete updates rather than a continuous flow.

  • steps()
  • snap
  • ticker

Parallax inspector

Continuous Linear

  • translateY
  • linear
  • duplicated stack

A steady linear scroll over a fixed hero scene. The translateY animation runs from 0 to -50% on a duplicated stack so the loop seam is invisible. Identical to the gallery thumbnail; this is the canonical baseline.

Helped you ship something? 🐟 Send my cat a churu

/* Steady linear scroll over the static hero scene; identical to the thumbnail baseline. */
.parallax-frame {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.parallax-scene {
  /* Hero scene — never moves. */
  position: absolute;
  inset: 0;
  z-index: 0;
}

.parallax-track {
  position: absolute;
  inset: 0;
  overflow: hidden;
  display: flex;
  justify-content: center;
}

.parallax-stack {
  /* Duplicated copies (×2) so translateY(-50%) lands on a seam. */
  position: absolute;
  top: 0;
  width: 66%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 0;
  animation: parallaxScroll 9.00s linear infinite;
}

@keyframes parallaxScroll {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .parallax-stack {
    animation: none;
    top: 50%;
    transform: translateY(-50%);
  }
}

How to make this

A fixed-background parallax baseline pins the scene in a fixed pseudo-layer and lets normal content scroll over it, with reduced-motion and mobile fallbacks that remove the fixed attachment.

html
<main class="parallax-page">  <section class="parallax-hero">    <p>Field note</p>    <h1>Fixed scene, moving foreground</h1>  </section>  <section class="parallax-card">    <h2>Foreground content</h2>    <p>The card scrolls while the backdrop remains visually pinned.</p>  </section></main> <style>13.parallax-page {  position: relative;  isolation: isolate;  min-height: 150vh;  color: #f8fafc;  overflow: clip;}.parallax-page::before {  content: "";22  position: fixed;  inset: 0;  z-index: -1;  background:    radial-gradient(circle at 24% 28%, rgba(167,139,250,.42), transparent 30%),    radial-gradient(circle at 78% 65%, rgba(34,211,238,.32), transparent 34%),    linear-gradient(135deg, #312e81, #0f172a 68%);  background-size: cover;30  transform: translateZ(0);}32.parallax-hero {  min-height: 72vh;  display: grid;  place-content: center;  padding: 2rem;  text-align: center;}.parallax-hero h1 {  max-width: 12ch;  margin: .25rem 0 0;  font: 800 clamp(2rem, 8vw, 5rem)/.95 ui-sans-serif, system-ui;}.parallax-card {  width: min(36rem, calc(100% - 2rem));  margin: 0 auto 20vh;  padding: 1.25rem;  border-radius: 16px;  background: rgba(15, 23, 42, .78);  border: 1px solid rgba(248, 250, 252, .12);51  backdrop-filter: blur(10px);}53@media (prefers-reduced-motion: reduce) {  .parallax-page::before {    position: absolute;    min-height: 100%;  }}@supports (-webkit-touch-callout: none) {  .parallax-page::before { position: absolute; }}</style>

Annotated snippet

  1. Line 13The page creates the stacking context for the fixed scene. isolation keeps the negative z-index pseudo-layer behind this component without slipping behind the entire document.
  2. Line 22The backdrop is fixed to the viewport, not to a scrolling section. Foreground content moves normally, which produces the parallax read without JavaScript scroll listeners.

    Simulating scroll. Before the foreground card AND the scene both translate together — that is what background-attachment: scroll looks like and there is no depth read. After only the foreground card moves while the scene stays pinned — the card visibly slides over a stable backdrop, which is the parallax illusion.

    PitfallDoes fixed background parallax need JavaScript?

    Not for the baseline effect. Pin a scene layer and let regular document flow move the foreground. Add JavaScript only when you need measured scroll progress, synchronized timelines, or element-specific depth ratios.

  3. Line 30translateZ(0) encourages a composited layer for the fixed backdrop. Use it sparingly; the goal is one stable scene layer, not many promoted sections.
    PitfallCan parallax hurt performance?

    Yes. A viewport-sized fixed layer, blur, and many overlapping translucent elements can repaint heavily on scroll. Keep the fixed scene simple, avoid animating filters on it, and put small blur effects on foreground cards instead.

  4. Line 32The foreground uses ordinary document flow. Parallax should not require scroll event handlers for the baseline case.
    PitfallDoes fixed background parallax need JavaScript?

    Not for the baseline effect. Pin a scene layer and let regular document flow move the foreground. Add JavaScript only when you need measured scroll progress, synchronized timelines, or element-specific depth ratios.

  5. Line 51Backdrop blur belongs on the foreground card, not the fixed scene. That keeps the large viewport-sized background from repainting with a blur every scroll frame.

    A 12px blur paints the same pixels either way — but the surface area is what matters. before puts blur on the whole 280px scene, after puts the SAME blur on a small foreground card. Same visual result on the card, far less paint cost on every scroll frame.

    PitfallCan parallax hurt performance?

    Yes. A viewport-sized fixed layer, blur, and many overlapping translucent elements can repaint heavily on scroll. Keep the fixed scene simple, avoid animating filters on it, and put small blur effects on foreground cards instead.

  6. Line 53Reduced motion removes the fixed viewport attachment by making the scene absolute. The visual composition remains, but scrolling no longer creates a parallax mismatch.
    PitfallHow should parallax respect prefers-reduced-motion?

    Remove the fixed attachment or freeze scroll-linked movement. The page should still have the same content and visual hierarchy, but it should not create depth motion while the user scrolls.

Other pitfalls

Why not use background-attachment: fixed for every parallax section?
background-attachment: fixed is simple on desktop but inconsistent or disabled in some mobile browser paths. A fixed pseudo-layer gives you more control over fallback behavior, stacking, and reduced-motion overrides.
What is the mobile fallback for fixed background parallax?
Use a normal absolute or scrolling background on touch-heavy browsers, or replace the effect with a static hero image. Mobile viewport resizing and address-bar movement make fixed backgrounds harder to keep stable.

Notes

Overview

Fixed-background parallax is the oldest depth illusion on the web: pin one layer in place with background-attachment: fixed (or by ducking the layer outside the scrolling element entirely), and let the foreground scroll over it. The disparity between “moving” and “staying” reads as depth without any 3D math. This pattern reimplements the effect without browser background-attachment quirks by anchoring an SVG hero layer at z-index: 0 and translating a duplicated card stack on top via translateY.

When to use it

Reach for fixed-background parallax when you have a strong static composition (illustration, photo, gradient artwork) that you want to anchor while content scrolls past it — landing pages, story sections, editorial headers. Skip it for dashboards or any layout where each fold owns its own header; the parallax becomes noise when there is nothing to be parallaxing past. Skip it on mobile Safari too unless you have tested the specific iOS version — background-attachment has been quietly broken there for years.

How it works

The pattern uses a duplicated card stack: the same N cards repeat twice inside a single .parallax-stack element. The stack animates from translateY(0) to translateY(-50%) on an infinite loop. Because the bottom half of the stack is an exact copy of the top half, the moment the animation reaches -50% the view is identical to 0% — the loop seam is invisible. The hero scene sits at z-index: 0 inside the parallax frame and never moves, so the depth illusion comes entirely from the foreground stack’s translation against a static backdrop.

Production gotchas

On mobile Safari, background-attachment: fixed simply does not work — the background scrolls with the foreground anyway. The duplicated-stack approach in this pattern sidesteps the property entirely, which is why it ships as the baseline. translateY(-50%) only lands on a clean seam if the stack height is an exact multiple of one card’s height — if you change card sizes mid-list, the loop will visibly jump every cycle. Browsers throttle non-RAF-driven scroll animations when the tab is backgrounded; this is fine here because the stack uses a CSS keyframe (browser-native), not a JS scroll handler.

Accessibility

Parallax motion is one of the most common vestibular triggers, so the prefers-reduced-motion: reduce branch in this pattern pins the stack at translateY(-50%) so a representative middle slice is visible without movement. The hero scene is decorative (aria-hidden="true"); the real content for screen readers lives in the cards above it.

References

Implementation depth

Treat fixed-background parallax as a depth illusion with browser caveats, especially on mobile Safari. A duplicated foreground stack is often more reliable than depending on background-attachment: fixed across every viewport and input mode.

The important production check is the loop boundary. If the duplicated stack height does not divide cleanly, the foreground jumps even though the background is static. Under reduced motion, pin the stack at a representative state instead of removing the scene entirely.