Parallax Effect
Original · freeImage parallaxes within its frame as you scroll.
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/imgfx-parallax.json"use client";
import { useRef } from "react";
import {
motion,
useScroll,
useTransform,
useReducedMotion,
} from "motion/react";
export default function ImgfxParallax() {
const reduce = useReducedMotion();
const frameRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: frameRef,
offset: ["start end", "end start"],
});
// Image drifts vertically within its clipped frame as the frame crosses the viewport.
const imageY = useTransform(
scrollYProgress,
[0, 1],
reduce ? ["0%", "0%"] : ["-14%", "14%"],
);
// A subtler counter-drift on the caption for depth separation.
const captionY = useTransform(
scrollYProgress,
[0, 1],
reduce ? ["0%", "0%"] : ["8%", "-8%"],
);
const sheenX = useTransform(
scrollYProgress,
[0, 1],
reduce ? ["0%", "0%"] : ["-40%", "140%"],
);
return (
<section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-24 text-neutral-900 sm:px-10 lg:py-32 dark:bg-neutral-950 dark:text-neutral-100">
<style>{`
@keyframes imgfxpx_float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
.imgfxpx_float { animation: none !important; }
}
`}</style>
{/* atmosphere */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-70 [background:radial-gradient(60%_50%_at_50%_0%,theme(colors.indigo.200/.45),transparent_70%)] dark:opacity-40 dark:[background:radial-gradient(60%_50%_at_50%_0%,theme(colors.indigo.500/.28),transparent_70%)]"
/>
<div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-neutral-300/70 bg-white/60 px-3.5 py-1.5 text-[11px] font-semibold uppercase tracking-[0.22em] text-indigo-700 backdrop-blur dark:border-white/10 dark:bg-white/5 dark:text-indigo-300">
<span className="imgfxpx_float inline-block h-1.5 w-1.5 rounded-full bg-indigo-500" />
Parallax Frame
</span>
<h2 className="mt-6 text-balance text-4xl font-semibold tracking-tight sm:text-5xl">
The image drifts inside its window
</h2>
<p className="mt-4 max-w-xl text-pretty text-base leading-relaxed text-neutral-600 dark:text-neutral-400">
Scroll and the photograph glides against a fixed crop, so the frame
holds still while the world behind it moves.
</p>
</div>
<div className="relative mx-auto mt-16 max-w-4xl">
{/* the fixed frame — image is oversized and translated within */}
<motion.div
ref={frameRef}
className="group relative aspect-[16/10] overflow-hidden rounded-3xl border border-neutral-900/10 bg-neutral-200 shadow-[0_30px_80px_-30px_rgba(30,27,75,0.45)] ring-1 ring-white/40 dark:border-white/10 dark:bg-neutral-800 dark:shadow-[0_30px_90px_-30px_rgba(0,0,0,0.8)] dark:ring-white/5"
>
<motion.div style={{ y: imageY }} className="absolute -inset-y-[20%] inset-x-0">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/img/gallery/g14.webp"
alt="Mountain ridge fading into layered morning haze"
loading="lazy"
draggable={false}
className="h-full w-full object-cover"
/>
</motion.div>
{/* legibility + depth veil */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 bg-gradient-to-t from-neutral-950/55 via-neutral-950/5 to-transparent"
/>
{/* moving sheen tied to scroll */}
<motion.div
aria-hidden
style={{ x: sheenX }}
className="pointer-events-none absolute inset-y-0 -left-1/2 w-1/2 skew-x-[-12deg] bg-gradient-to-r from-transparent via-white/25 to-transparent"
/>
{/* inner edge sharpening */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 rounded-3xl ring-1 ring-inset ring-white/15"
/>
{/* caption rides a gentle counter-parallax */}
<motion.div
style={{ y: captionY }}
className="absolute inset-x-5 bottom-5 flex items-end justify-between gap-4 sm:inset-x-7 sm:bottom-7"
>
<div className="text-left">
<p className="text-xs font-medium uppercase tracking-[0.2em] text-white/70">
Frame 14 / 36
</p>
<p className="mt-1 text-lg font-semibold text-white drop-shadow-sm sm:text-xl">
Ridgeline at first light
</p>
</div>
<span className="hidden shrink-0 rounded-full border border-white/25 bg-white/10 px-3 py-1 text-xs font-medium text-white/85 backdrop-blur sm:inline-block">
depth · scroll
</span>
</motion.div>
</motion.div>
<p className="mt-5 text-center text-sm text-neutral-500 dark:text-neutral-500">
Keep scrolling to feel the vertical drift — the crop never moves.
</p>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Hover Zoom Effect
OriginalImage zooms in smoothly inside a fixed rounded frame on hover.

Hover Zoom Out Effect
OriginalImage starts zoomed and settles to normal on hover.

Zoom Rotate Effect
OriginalImage zooms and rotates slightly on hover.

Grayscale Color Effect
OriginalGreyscale image turns to full colour on hover.

Color Grayscale Effect
OriginalFull-colour image fades to greyscale on hover.

Blur Sharp Effect
OriginalBlurred image sharpens on hover.

Sharp Blur Effect
OriginalSharp image blurs with a caption appearing on hover.

Sepia Clear Effect
OriginalSepia-toned image clears to full colour on hover.

Saturate Pop Effect
OriginalMuted image saturates and pops on hover.

Contrast Pop Effect
OriginalLow-contrast image gains punchy contrast on hover.

Brightness Lift Effect
OriginalDim image brightens on hover.

Hue Rotate Effect
OriginalImage hue-shifts through colours on hover.

