22 CSS Parallax Effects04 / 22

Pure CSSMIT licensed

Split Screen Dual Axis Parallax Scroll

Two columns that travel in opposite directions: the portrait side rises while the prose side sinks, so the spread shears apart as the reader scrolls and closes again on the way back. Built for fashion and interview features where the pairing of image and text is the layout, and it collapses to a plain single column below 720px.

Published

Live Demo
Try it

The code

<div class="plx-04">
  <header class="plx-04__head">
    <p class="plx-04__issue">Maison Verrier — Autumn dossier</p>
    <h1 class="plx-04__h1">Sam Rivera on cutting a coat that has to last nine winters</h1>
  </header>
  <section class="plx-04__stage">
    <div class="plx-04__col plx-04__col--up">
      <figure class="plx-04__figure">
        <img class="plx-04__plate" src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1000&auto=format&fit=crop" alt="Studio portrait against a dark grey backdrop" loading="lazy" decoding="async">
        <figcaption class="plx-04__caption">Sam Rivera, fitting room, Rue Bertin.</figcaption>
      </figure>
      <blockquote class="plx-04__quote">
        <p>"A coat is not a season. If it looks like this year, it will look like this year forever."</p>
      </blockquote>
    </div>
    <div class="plx-04__col plx-04__col--down">
      <p class="plx-04__p">The workroom keeps two patterns for every coat: the one the client is measured against, and the one it will be let out to in five years. Rivera considers the second the real garment.</p>
      <p class="plx-04__p">"The shoulder is where you decide," they say. "Everything else can be argued with later. A shoulder that is wrong is wrong for the life of the cloth."</p>
      <p class="plx-04__p">The house cuts eleven coats a month, which is fewer than it made in 1996 and, Rivera insists, more than it should. Each takes about thirty hours across three fittings, and the third fitting exists mostly to be told that nothing needs changing.</p>
      <p class="plx-04__p">Wool arrives from two mills, both in the same valley, and is rested for a season before it is touched. "Cloth has an opinion when it is new," Rivera says. "You wait for it to calm down."</p>
    </div>
  </section>
  <footer class="plx-04__foot">
    <p class="plx-04__p">Continued in the print dossier. Interview by Priya Sharma.</p>
  </footer>
</div>
.plx-04 {
  width: 100%;
  min-height: 100vh;
  display: block;
  position: relative;
  box-sizing: border-box;
  --font-display: "Didot", "Playfair Display", "Hoefler Text", Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", sans-serif;
  --paper: #fbfbfa;
  --ink: #0b0b0b;
  --muted: #6e6e6e;
  --rule: #e0dedb;
  --accent: #b0121f;
  --shear: clamp(40px, 9vw, 150px);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  overflow-x: clip;
}

.plx-04 * {
  box-sizing: border-box;
}

.plx-04__head {
  padding: clamp(40px, 8vw, 110px) clamp(20px, 5vw, 72px) clamp(30px, 5vw, 60px);
  max-inline-size: 52rem;
}

.plx-04__issue {
  margin: 0 0 22px;
  font: 500 10px/1 var(--font-body);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted);
}

.plx-04__h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(30px, 6.4vw, 62px);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.plx-04__stage {
  position: relative;
  overflow: clip;
  display: grid;
  gap: clamp(28px, 5vw, 64px);
  padding: calc(var(--shear) + 24px) clamp(20px, 5vw, 72px);
  border-block: 1px solid var(--rule);
}

.plx-04__col {
  min-inline-size: 0;
  will-change: transform;
}

.plx-04__figure {
  margin: 0 0 28px;
}

.plx-04__plate {
  display: block;
  inline-size: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: #1a1816;
}

.plx-04__caption {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--muted);
}

.plx-04__quote {
  margin: 0;
}

.plx-04__quote p {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 4.4vw, 32px);
  font-style: italic;
  line-height: 1.24;
  color: var(--accent);
  text-wrap: pretty;
}

.plx-04__p {
  margin: 0 0 20px;
  max-inline-size: 34rem;
  font-size: clamp(15px, 3.4vw, 17px);
  line-height: 1.78;
  color: #262625;
  text-wrap: pretty;
}

.plx-04__col--down .plx-04__p:first-child::first-letter {
  float: left;
  font-family: var(--font-display);
  font-size: 3.4em;
  line-height: 0.82;
  padding-inline-end: 0.08em;
  color: var(--ink);
}

.plx-04__foot {
  padding: clamp(40px, 8vw, 96px) clamp(20px, 5vw, 72px) 120px;
}

.plx-04__foot .plx-04__p {
  color: var(--muted);
}

@media (min-width: 720px) {
  .plx-04__stage {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  }

  @supports (animation-timeline: scroll()) {
    .plx-04__col--up {
      animation: plx-04-up linear both;
      animation-timeline: scroll(root);
    }

    .plx-04__col--down {
      animation: plx-04-down linear both;
      animation-timeline: scroll(root);
    }
  }
}

@keyframes plx-04-up {
  from {
    transform: translateY(var(--shear));
  }

  to {
    transform: translateY(calc(var(--shear) * -1));
  }
}

@keyframes plx-04-down {
  from {
    transform: translateY(calc(var(--shear) * -1));
  }

  to {
    transform: translateY(var(--shear));
  }
}

@media (prefers-reduced-motion: reduce) {
  .plx-04__col {
    animation: none !important;
    transform: none !important;
    will-change: auto;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Parallax Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Split Screen Dual Axis Parallax Scroll
Source: https://codefronts.com/motion/css-parallax-designs/split-screen-dual-axis-parallax-scroll/

Two columns that travel in opposite directions: the portrait side rises while the prose side sinks, so the spread shears apart as the reader scrolls and closes again on the way back. Built for fashion and interview features where the pairing of image and text is the layout, and it collapses to a plain single column below 720px.
## HTML
```html
<div class="plx-04">
  <header class="plx-04__head">
    <p class="plx-04__issue">Maison Verrier — Autumn dossier</p>
    <h1 class="plx-04__h1">Sam Rivera on cutting a coat that has to last nine winters</h1>
  </header>
  <section class="plx-04__stage">
    <div class="plx-04__col plx-04__col--up">
      <figure class="plx-04__figure">
        <img class="plx-04__plate" src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1000&auto=format&fit=crop" alt="Studio portrait against a dark grey backdrop" loading="lazy" decoding="async">
        <figcaption class="plx-04__caption">Sam Rivera, fitting room, Rue Bertin.</figcaption>
      </figure>
      <blockquote class="plx-04__quote">
        <p>"A coat is not a season. If it looks like this year, it will look like this year forever."</p>
      </blockquote>
    </div>
    <div class="plx-04__col plx-04__col--down">
      <p class="plx-04__p">The workroom keeps two patterns for every coat: the one the client is measured against, and the one it will be let out to in five years. Rivera considers the second the real garment.</p>
      <p class="plx-04__p">"The shoulder is where you decide," they say. "Everything else can be argued with later. A shoulder that is wrong is wrong for the life of the cloth."</p>
      <p class="plx-04__p">The house cuts eleven coats a month, which is fewer than it made in 1996 and, Rivera insists, more than it should. Each takes about thirty hours across three fittings, and the third fitting exists mostly to be told that nothing needs changing.</p>
      <p class="plx-04__p">Wool arrives from two mills, both in the same valley, and is rested for a season before it is touched. "Cloth has an opinion when it is new," Rivera says. "You wait for it to calm down."</p>
    </div>
  </section>
  <footer class="plx-04__foot">
    <p class="plx-04__p">Continued in the print dossier. Interview by Priya Sharma.</p>
  </footer>
</div>
```
## CSS
```css
.plx-04 {
  width: 100%;
  min-height: 100vh;
  display: block;
  position: relative;
  box-sizing: border-box;
  --font-display: "Didot", "Playfair Display", "Hoefler Text", Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", sans-serif;
  --paper: #fbfbfa;
  --ink: #0b0b0b;
  --muted: #6e6e6e;
  --rule: #e0dedb;
  --accent: #b0121f;
  --shear: clamp(40px, 9vw, 150px);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  overflow-x: clip;
}

.plx-04 * {
  box-sizing: border-box;
}

.plx-04__head {
  padding: clamp(40px, 8vw, 110px) clamp(20px, 5vw, 72px) clamp(30px, 5vw, 60px);
  max-inline-size: 52rem;
}

.plx-04__issue {
  margin: 0 0 22px;
  font: 500 10px/1 var(--font-body);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted);
}

.plx-04__h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(30px, 6.4vw, 62px);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.plx-04__stage {
  position: relative;
  overflow: clip;
  display: grid;
  gap: clamp(28px, 5vw, 64px);
  padding: calc(var(--shear) + 24px) clamp(20px, 5vw, 72px);
  border-block: 1px solid var(--rule);
}

.plx-04__col {
  min-inline-size: 0;
  will-change: transform;
}

.plx-04__figure {
  margin: 0 0 28px;
}

.plx-04__plate {
  display: block;
  inline-size: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: #1a1816;
}

.plx-04__caption {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--muted);
}

.plx-04__quote {
  margin: 0;
}

.plx-04__quote p {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 4.4vw, 32px);
  font-style: italic;
  line-height: 1.24;
  color: var(--accent);
  text-wrap: pretty;
}

.plx-04__p {
  margin: 0 0 20px;
  max-inline-size: 34rem;
  font-size: clamp(15px, 3.4vw, 17px);
  line-height: 1.78;
  color: #262625;
  text-wrap: pretty;
}

.plx-04__col--down .plx-04__p:first-child::first-letter {
  float: left;
  font-family: var(--font-display);
  font-size: 3.4em;
  line-height: 0.82;
  padding-inline-end: 0.08em;
  color: var(--ink);
}

.plx-04__foot {
  padding: clamp(40px, 8vw, 96px) clamp(20px, 5vw, 72px) 120px;
}

.plx-04__foot .plx-04__p {
  color: var(--muted);
}

@media (min-width: 720px) {
  .plx-04__stage {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  }

  @supports (animation-timeline: scroll()) {
    .plx-04__col--up {
      animation: plx-04-up linear both;
      animation-timeline: scroll(root);
    }

    .plx-04__col--down {
      animation: plx-04-down linear both;
      animation-timeline: scroll(root);
    }
  }
}

@keyframes plx-04-up {
  from {
    transform: translateY(var(--shear));
  }

  to {
    transform: translateY(calc(var(--shear) * -1));
  }
}

@keyframes plx-04-down {
  from {
    transform: translateY(calc(var(--shear) * -1));
  }

  to {
    transform: translateY(var(--shear));
  }
}

@media (prefers-reduced-motion: reduce) {
  .plx-04__col {
    animation: none !important;
    transform: none !important;
    will-change: auto;
  }
}
```

How this works

Opposing motion is a sign flip, nothing more. One column animates to translateY(calc(var(--shear) * -1)) and the other to translateY(var(--shear)), both bound to scroll(root). Because the amplitudes are equal and opposite, the perceived separation at any scroll position is twice --shear, which makes the effect trivial to tune from a single custom property.

Reserve the travel or you will clip the content. A column that rises 140px leaves 140px of empty space at its bottom edge, and a column that sinks does the same at its top. The stage therefore carries vertical padding at least equal to --shear, and the shear value is clamped down on small viewports so the padding never dominates the layout.

Whitespace, not chrome, carries the design. There is no card, border or shadow in this demo: the two columns sit directly on the paper with a single hairline rule between them. Depth comes only from the differential motion, which is the point — parallax is strongest when nothing else on the page is competing to look three-dimensional.

Below the fold-out width, kill it. On a phone the two columns stack, and a stacked pair moving in opposite directions just reads as jitter, so the media query removes the animation entirely rather than reducing it. The trap is leaving the transform applied while removing the animation — the second column then sits permanently offset by its final frame.

Make it yours

  • Change --shear to set how far the two columns diverge.
  • Give the right column a smaller amplitude for an asymmetric drift.
  • Swap --accent to move the pull-quote and rule colour together.
  • Raise the breakpoint from 720px if your prose column needs more room.
  • Add animation-range: cover to scope the shear to the section rather than the page.
  • Replace the plate gradient with a real portrait once you have art.

Gotchas — read before shipping

  • Forgetting the reserved padding clips the trailing edge of whichever column moves up, and the crop only appears at the end of the scroll.
  • Stacking the columns on mobile while leaving the opposing animations on makes the page feel like it is fighting the finger — remove the animation at the breakpoint, and reset the transform with it.
  • Equal amplitudes on unequal column heights make the shorter column run out of content mid-scroll; match the copy lengths or clamp the shorter side's travel.
  • An overflow: hidden wrapper without a height still lets the shear escape at the bottom — the stage needs both the clip and the reserved space.

Browser support

ChromeSafariFirefoxEdge
115+18+130+115+

Only the scroll-timeline binding is modern; the layout is plain grid. Without support the two columns render aligned and static, which is a perfectly good editorial spread.

Techniques used in this demo

Search CodeFronts

Loading…