All components
Container Scroll Animation
effectsAceternity UI component.
responsive · 660px
Install
Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:
$
npx shadcn@latest add https://your-domain/r/container-scroll-animation.jsonUsage
"use client";
import { ContainerScroll } from "@/registry/aceternity-ui/container-scroll-animation";
export default function Demo() {
return (
<ContainerScroll
titleComponent={
<div className="space-y-3 text-center">
<p className="text-sm font-medium text-muted-foreground uppercase tracking-widest">
Introducing
</p>
<h1 className="text-4xl md:text-5xl font-bold text-foreground">
Scroll into view
</h1>
<p className="text-muted-foreground max-w-md mx-auto text-base">
Scroll down to watch the card rotate from a dramatic 3-D tilt into
a flat, full-bleed presentation.
</p>
</div>
}
>
<div className="h-full w-full flex flex-col items-center justify-center gap-6 p-6 bg-background rounded-xl">
<div className="grid grid-cols-3 gap-3 w-full max-w-lg">
{Array.from({ length: 9 }).map((_, i) => (
<div
key={i}
className="aspect-square rounded-lg bg-muted flex items-center justify-center text-muted-foreground text-xs font-mono"
>
{String(i + 1).padStart(2, "0")}
</div>
))}
</div>
<p className="text-muted-foreground text-sm text-center max-w-xs">
Component content renders here once the container is fully flat.
</p>
</div>
</ContainerScroll>
);
}Component source
"use client";
import React, { useRef } from "react";
import { useScroll, useTransform, motion, MotionValue } from "motion/react";
export const ContainerScroll = ({
titleComponent,
children,
}: {
titleComponent: string | React.ReactNode;
children: React.ReactNode;
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: containerRef,
});
const [isMobile, setIsMobile] = React.useState(false);
React.useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth <= 768);
};
checkMobile();
window.addEventListener("resize", checkMobile);
return () => {
window.removeEventListener("resize", checkMobile);
};
}, []);
const scaleDimensions = () => {
return isMobile ? [0.7, 0.9] : [1.05, 1];
};
const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions());
const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
return (
<div
className="h-[60rem] md:h-[80rem] flex items-center justify-center relative p-2 md:p-20"
ref={containerRef}
>
<div
className="py-10 md:py-40 w-full relative"
style={{
perspective: "1000px",
}}
>
<Header translate={translate} titleComponent={titleComponent} />
<Card rotate={rotate} translate={translate} scale={scale}>
{children}
</Card>
</div>
</div>
);
};
export const Header = ({ translate, titleComponent }: any) => {
return (
<motion.div
style={{
translateY: translate,
}}
className="div max-w-5xl mx-auto text-center"
>
{titleComponent}
</motion.div>
);
};
export const Card = ({
rotate,
scale,
children,
}: {
rotate: MotionValue<number>;
scale: MotionValue<number>;
translate: MotionValue<number>;
children: React.ReactNode;
}) => {
return (
<motion.div
style={{
rotateX: rotate,
scale,
boxShadow:
"0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003",
}}
className="max-w-5xl -mt-12 mx-auto h-[30rem] md:h-[40rem] w-full border-4 border-[#6C6C6C] p-2 md:p-6 bg-[#222222] rounded-[30px] shadow-2xl"
>
<div className=" h-full w-full overflow-hidden rounded-2xl bg-gray-100 dark:bg-zinc-900 md:rounded-2xl md:p-4 ">
{children}
</div>
</motion.div>
);
};Dependencies
motion
Source: Aceternity UI