Sticky Scroll Features
Sticky Scroll Features component with smooth animations and modern UI.
Installation
Add this component to your project using the CLI:
terminal
npx vui-registry-cli-v1 add sticky-scroll-featuresSource Code
sticky-scroll-features.tsx
"use client"
import React, { useEffect, useRef, useState } from "react"
import { motion, useScroll, useMotionValueEvent } from "framer-motion"
import { cn } from "@/lib/utils"
import { Layout, GitBranch, Terminal, Globe, Zap, Layers, Code2, Cpu } from "lucide-react"
const content = [
{
title: "Collaborative Editing",
description:
"Work together in real-time with your team, clients, and stakeholders. Collaborate on every aspect of your project, from design to deployment.",
content: (
<div className="h-full w-full bg-[linear-gradient(to_bottom_right,var(--cyan-500),var(--emerald-500))] flex items-center justify-center text-white">
<Layout className="h-10 w-10" />
</div>
),
},
{
title: "Real time changes",
description:
"See changes as they happen. With our platform, you can track every modification in real time. No more confusion about the latest version of your project.",
content: (
<div className="h-full w-full flex items-center justify-center text-white">
<GitBranch className="h-10 w-10" />
</div>
),
},
{
title: "Version control",
description:
"Experience real-time updates and never stress about version control again. Our platform ensures that you're always working on the most recent version of your project.",
content: (
<div className="h-full w-full bg-[linear-gradient(to_bottom_right,var(--orange-500),var(--yellow-500))] flex items-center justify-center text-white">
<Terminal className="h-10 w-10" />
</div>
),
},
{
title: "Running out of content",
description:
"Experience real-time updates and never stress about version control again. Our platform ensures that you're always working on the most recent version of your project.",
content: (
<div className="h-full w-full bg-[linear-gradient(to_bottom_right,var(--cyan-500),var(--emerald-500))] flex items-center justify-center text-white">
<Globe className="h-10 w-10" />
</div>
),
},
]
export default function StickyScrollFeatures() {
const [activeCard, setActiveCard] = useState(0)
const ref = useRef<HTMLDivElement>(null)
const { scrollYProgress } = useScroll({
// target: ref,
container: ref,
offset: ["start start", "end start"],
})
const cardLength = content.length
useMotionValueEvent(scrollYProgress, "change", (latest) => {
const cardsBreakpoints = content.map((_, index) => index / cardLength)
const closest = cardsBreakpoints.reduce((prev, curr) => {
return Math.abs(curr - latest) < Math.abs(prev - latest) ? curr : prev
})
const index = cardsBreakpoints.indexOf(closest)
setActiveCard(index)
})
const backgroundColors = [
"bg-slate-900",
"bg-black",
"bg-neutral-900",
]
const linearGradients = [
"linear-gradient(to bottom right, var(--cyan-500), var(--emerald-500))",
"linear-gradient(to bottom right, var(--pink-500), var(--indigo-500))",
"linear-gradient(to bottom right, var(--orange-500), var(--yellow-500))",
"linear-gradient(to bottom right, var(--red-500), var(--orange-500))",
]
return (
<motion.div
animate={{
backgroundColor: backgroundColors[activeCard % backgroundColors.length],
}}
className="h-[30rem] w-full overflow-y-auto flex justify-center relative space-x-10 rounded-3xl p-10 scrollbar-hide font-sans transition-colors duration-500"
ref={ref}
>
<div className="div relative flex items-start px-4">
<div className="max-w-2xl">
{content.map((item, index) => (
<div key={item.title + index} className="my-20">
<motion.h2
initial={{ opacity: 0 }}
animate={{ opacity: activeCard === index ? 1 : 0.3 }}
className="text-2xl font-bold text-slate-100"
>
{item.title}
</motion.h2>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: activeCard === index ? 1 : 0.3 }}
className="text-kg text-slate-300 max-w-sm mt-10"
>
{item.description}
</motion.p>
</div>
))}
<div className="h-40" />
</div>
</div>
<motion.div
animate={{
background: linearGradients[activeCard % linearGradients.length],
}}
className={cn(
"hidden lg:block h-60 w-80 rounded-2xl bg-white sticky top-10 overflow-hidden",
content[activeCard].content ?? "bg-transparent"
)}
>
{content[activeCard].content}
</motion.div>
</motion.div>
)
}
