Stacked Cards Section

Click here to see the live demo.

  • Next.js
  • Typescript
  • GSAP
  • ScrollTrigger Plugin
  • useGSAP Plugin
  • Tailwind CSS
components/stacked-cards-section.tsx
"use client";
import gsap from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import { useGSAP } from "@gsap/react";
import { useRef } from "react";

gsap.registerPlugin(useGSAP, ScrollTrigger);

const PROCESS_DATA = [
  {
    step: "1",
    title: "Sketch Design",
  },
  {
    step: "2",
    title: "Design Development",
  },
  {
    step: "3",
    title: "Development Application",
  },
  {
    step: "4",
    title: "Interior Design",
  },
  {
    step: "5",
    title: "Building approval plans",
  },
  {
    step: "6",
    title: "Construction plans",
  },
];

export function StackedCards() {
  const containerRef = useRef<HTMLElement>(null);

  useGSAP(
    () => {
      if (!containerRef.current) return;

      const cards = containerRef.current.querySelectorAll(".card");

      cards.forEach((card, index) => {
        if (index >= PROCESS_DATA.length - 1) return;

        const cardInner = card.querySelector(".card-inner");

        gsap.fromTo(
          cardInner,
          {
            y: "0%",
            z: 0,
            rotationX: 0,
            opacity: 1,
          },
          {
            y: "-5%",
            z: -50,
            rotationX: 15,
            opacity: 0,
            scrollTrigger: {
              trigger: cards[index + 1],
              start: "top 75%",
              end: `bottom 60%`,
              scrub: true,
              pin: card,
              pinSpacing: false,
            },
          },
        );
      });
    },
    { scope: containerRef },
  );

  return (
    <section ref={containerRef}>
      <div className="relative">
        <div className="h-[75svh] bg-background border-b">
          <h1 className="text-9xl px-5 py-20 font-semibold">OUR PROCESS</h1>
        </div>
        {PROCESS_DATA.map((process) => (
          <div
            key={process.title}
            className="card flex justify-center items-center transform-3d perspective-distant will-change-transform h-[75svh]"
          >
            <div className="card-inner relative w-full h-full bg-background origin-[50%_100%] will-change-transform overflow-hidden p-10 grid grid-cols-2">
              <p className="text-9xl font-semibold">[{process.step}]</p>
              <h2 className="text-6xl font-semibold tracking-tight uppercase">
                {process.title}
              </h2>
            </div>
          </div>
        ))}
        <div className="bg-background h-svh flex justify-center items-center border-t">
          <p className="text-8xl font-serif">Fin</p>
        </div>
      </div>
    </section>
  );
}