Parallax

Layered depth on scroll, tight and controlled.

A long scroll of cards where the images drift slower than the copy. Keep the speeds small so the depth stays quiet.

Token ready

Quiet surfaces that still feel alive when you move through them.

Soft depth

Images lag behind the scroll so the stack feels layered, not flat.

Keep it tight

Small speed values stay calm. Push them only when the moment needs it.

Ship the cut

A long frame of cards, each with its own slow image drift.

Stay composed

Scroll the list. Watch the pictures move a little slower than the copy.

1
0.45
tsx
import { Parallax, ParallaxLayer } from "@/components/motion/parallax"const CARDS = [  {    "title": "Token ready",    "body": "Quiet surfaces that still feel alive when you move through them.",    "image": "/assets/brand/demos/attachments/attachment-1.png"  },  {    "title": "Soft depth",    "body": "Images lag behind the scroll so the stack feels layered, not flat.",    "image": "/assets/brand/demos/attachments/attachment-2.png"  },  {    "title": "Keep it tight",    "body": "Small speed values stay calm. Push them only when the moment needs it.",    "image": "/assets/brand/demos/attachments/attachment-3.png"  },  {    "title": "Ship the cut",    "body": "A long frame of cards, each with its own slow image drift.",    "image": "/assets/brand/demos/attachments/attachment-4.png"  },  {    "title": "Stay composed",    "body": "Scroll the list. Watch the pictures move a little slower than the copy.",    "image": "/assets/brand/demos/attachments/attachment-5.png"  }]export function Example() {  return (    <Parallax      strength={1}      className="h-[38rem] w-full"    >      <div className="mx-auto flex w-full max-w-[300px] flex-col gap-14 py-12">        {CARDS.map((card) => (          <article            key={card.title}            className="overflow-hidden rounded-xl border border-border bg-background"          >            <div className="relative aspect-[4/3] overflow-hidden">              <ParallaxLayer                speed={0.45}                className="absolute inset-0"              >                <img                  src={card.image}                  alt=""                  className="size-full object-cover"                />              </ParallaxLayer>            </div>            <div className="px-3.5 py-3.5">              <h3 className="text-sm font-medium tracking-wide">                {card.title}              </h3>              <p className="mt-1 text-xs tracking-wide text-muted-foreground">                {card.body}              </p>            </div>          </article>        ))}      </div>    </Parallax>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add parallax

Usage

import { Parallax, ParallaxLayer } from "@/components/motion/parallax"
<Parallax strength={1} className="h-96 w-full">  <div className="flex flex-col gap-6 px-4 py-8">    <article className="overflow-hidden rounded-xl border">      <div className="relative aspect-[16/10] overflow-hidden">        <ParallaxLayer speed={0.45} className="absolute inset-0">          <img src="/photo.jpg" alt="" className="size-full object-cover" />        </ParallaxLayer>      </div>      <div className="px-4 py-4">        <h3>Token ready</h3>        <p>Images lag behind the scroll.</p>      </div>    </article>  </div></Parallax>

API Reference

Parallax

PropTypeDefault
strengthnumber1
classNamestring—
childrenReactNode—

Also accepts the other native div attributes. Scroll happens on this element.

ParallaxLayer

PropTypeDefault
speednumber0.35
classNamestring—
childrenReactNode—

Put the layer inside an overflow-hidden frame. The layer scales itself so the image always covers while it drifts.