Parallax Scroll Story
A captivating, premium multi-section scroll experience where images and text reveal themselves with depth and parallax effects.
Installation
Add this component to your project using the CLI:
npx vui-registry-cli-v1 add parallax-scroll-storySource Code
"use client"
import { useRef } from "react"
import { motion, useScroll, useTransform, useSpring } from "framer-motion"
import { Camera, MoveRight, Scan, Aperture, Globe } from "lucide-react" // Fixed: Added Globe here
const DATA = [
{
title: "TOKYO",
subtitle: "STRUCTURAL GENOME",
location: "35.6895° N, 139.6917° E",
desc: "The architecture of Tokyo is a silent dialogue between the ancient and the hyper-modern. Every steel beam and glass pane is placed with a precision that borders on the divine. In Shinjuku, the skyline becomes a living blueprint of human ambition.",
img: "https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?q=80&w=2600"
},
{
title: "ALPS",
subtitle: "GLACIAL MONOLITH",
location: "46.5851° N, 7.9610° E",
desc: "There is a profound weight to the silence found at the summit of the Alps. It is a place where time is measured in centuries, not seconds. The jagged peaks stand as unyielding sentinels against the sky, their granite faces weathered.",
img: "https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=2600"
},
{
title: "NAMIB",
subtitle: "THE RADIANT VOID",
location: "24.7275° S, 15.3413° E",
desc: "The dunes of the Namib are moving sculptures, shaped by a wind that has been blowing since the dawn of time. At dawn, the sands turn a deep, impossible orange, casting shadows that stretch like ink across the valley floor.",
img: "https://images.unsplash.com/photo-1473580044384-7ba9967e16a0?q=80&w=2600"
},
{
title: "KYOTO",
subtitle: "ETERNAL SERENITY",
location: "35.0116° N, 135.7681° E",
desc: "Kyoto is where time slows to the rhythm of falling cherry blossoms. The wooden temples and moss-covered gardens tell stories of a thousand years, where every stone is a testament to the pursuit of perfect harmony.",
img: "https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e?q=80&w=2600"
}
]
export default function SeamlessBoutique() {
return (
<main className="relative bg-[#080808] text-[#f0f0f0] font-sans antialiased overflow-x-hidden">
{/* BOUTIQUE FILM GRAIN */}
<div className="fixed inset-0 pointer-events-none z-[200] opacity-[0.12] mix-blend-soft-light">
<svg width="100%" height="100%">
<filter id="boutiqueGrain">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" stitchTiles="stitch" />
<feColorMatrix type="saturate" values="0" />
<feComponentTransfer>
<feFuncR type="linear" slope="1.2" />
<feFuncG type="linear" slope="1.2" />
<feFuncB type="linear" slope="1.2" />
</feComponentTransfer>
</filter>
<rect width="100%" height="100%" filter="url(#boutiqueGrain)" />
</svg>
</div>
{/* HEADER HUD */}
<nav className="fixed top-0 w-full px-6 md:px-12 py-6 md:py-10 flex justify-between items-start z-[150] mix-blend-difference">
<div className="flex flex-col gap-1">
<span className="font-black text-2xl md:text-3xl tracking-tighter leading-none italic uppercase text-white">Archive®</span>
<span className="text-[7px] md:text-[9px] font-mono opacity-40 tracking-[0.5em] uppercase">Visual_Data_Engine</span>
</div>
<div className="flex gap-4 md:gap-8 items-center">
<span className="hidden md:block text-[9px] font-black tracking-widest opacity-30 uppercase">Process: [0.03_Sync]</span>
<Scan className="w-4 h-4 md:w-5 md:h-5 text-white opacity-60" />
</div>
</nav>
{/* SEAMLESS SECTIONS */}
{DATA.map((item, i) => (
<InterlockSection key={i} item={item} index={i} />
))}
</main>
)
}
function InterlockSection({ item, index }: any) {
const container = useRef(null)
const { scrollYProgress } = useScroll({
target: container,
offset: ["start end", "end start"]
})
// MOTION CALCULATIONS - Tightened for better preview fit
const y = useTransform(scrollYProgress, [0, 1], ["-8%", "8%"])
const titleY = useTransform(scrollYProgress, [0, 1], ["40px", "-40px"])
const descY = useTransform(scrollYProgress, [0, 1], ["20px", "-20px"])
const scale = useTransform(scrollYProgress, [0, 0.5, 1], [1.02, 1, 1.02])
const opacity = useTransform(scrollYProgress, [0, 0.2, 0.8, 1], [0, 1, 1, 0])
return (
<section ref={container} className="relative h-screen min-h-[600px] w-full overflow-hidden border-b border-white/5">
{/* BACKGROUND PLATE */}
<motion.div style={{ scale, opacity }} className="absolute inset-0 w-full h-full">
<motion.img
style={{ y }}
src={item.img}
alt={item.title}
className="w-full h-[115%] object-cover grayscale-[0.4] brightness-[0.4] contrast-[1.15]"
/>
<div className="absolute inset-0 bg-gradient-to-t from-[#080808] via-transparent to-[#080808]/80" />
</motion.div>
{/* PRECISION GRID CONTENT */}
<div className="relative z-10 w-full h-full flex items-center justify-center px-6 md:px-12">
<div className="w-full max-w-[1400px] grid grid-cols-12 gap-6 md:gap-0 items-center md:items-end pb-12 md:pb-24">
<div className="col-span-12 lg:col-span-7">
<motion.div style={{ y: titleY }} className="space-y-2 md:space-y-4">
<div className="flex items-center gap-4 text-white/30 mb-2 md:mb-6">
<span className="text-[8px] md:text-[10px] font-mono tracking-[0.6em] uppercase">Record // 0{index + 1}</span>
<div className="h-[1px] flex-1 bg-white/10" />
</div>
<h2 className="text-[10vw] md:text-[7vw] font-black leading-[0.8] tracking-[-0.07em] uppercase italic m-0 break-words">
{item.title}
</h2>
<p className="text-[8px] md:text-[9px] font-black uppercase tracking-[0.5em] md:tracking-[0.8em] text-white/20 pt-1 md:pt-3">
{item.subtitle}
</p>
</motion.div>
</div>
<div className="col-span-12 lg:col-span-5 flex flex-col justify-end lg:pl-12">
<motion.div style={{ y: descY }} className="space-y-6 md:space-y-10">
<p className="text-sm md:text-lg lg:text-xl text-neutral-300 font-light leading-[1.4] tracking-tight border-l-2 border-white/10 pl-5 md:pl-8 max-w-md md:max-w-none">
{item.desc}
</p>
<div className="flex flex-col gap-4 md:gap-6 pl-5 md:pl-8">
<div className="flex items-center gap-4 md:gap-6 group cursor-pointer w-fit">
<span className="text-[8px] md:text-[9px] font-black uppercase tracking-[0.4em] border-b border-white/40 pb-1 group-hover:border-white transition-colors">
Initialize Sequence
</span>
<MoveRight className="w-4 h-4 md:w-5 md:h-5 group-hover:translate-x-3 transition-transform duration-500" />
</div>
<div className="flex items-center gap-3 text-white/20 font-mono text-[7px] md:text-[8px] tracking-widest uppercase">
<Globe className="w-3 h-3" />
<span>LOC: {item.location}</span>
</div>
</div>
</motion.div>
</div>
</div>
</div>
{/* TECH DATA HUD CORNERS */}
<div className="absolute bottom-6 md:bottom-10 left-6 md:left-10 hidden sm:flex items-center gap-6 opacity-20 text-[7px] md:text-[8px] font-mono tracking-widest uppercase italic">
<div className="flex flex-col">
<span>ISO_3200</span>
<span>f/2.8_LENS</span>
</div>
<Aperture className="w-3 h-3 md:w-4 md:h-4" />
</div>
<div className="absolute bottom-6 md:bottom-10 right-6 md:right-10 flex gap-3 mix-blend-difference text-white/50">
<Camera className="w-3 h-3 md:w-4 md:h-4 opacity-40" />
<span className="text-[7px] md:text-[8px] font-mono tracking-widest italic opacity-40 uppercase">Optical_Log_2026</span>
</div>
</section>
)
}Dependencies
framer-motion: latestclsx: latesttailwind-merge: latest
Props
Component property reference.
| Name | Type | Default | Description |
|---|---|---|---|
| data | Array<{title, subtitle, location, desc, img}> | DATA | The content items to display. |
Most components here are inspired by outstanding libraries and creators in the ecosystem. I don’t claim to be the original author — this is my space for learning, rebuilding, and understanding great work at a deeper level.
I’m still a student of the craft, constantly studying the best and translating what I learn through my own perspective. Every piece reflects curiosity, respect for the community, and small creative touches that feel true to me.
I’ve done my best to credit inspirations properly. If anything is missing or inaccurate, I truly appreciate a message so it can be corrected with care.

