Back

Scroll Scramble Section

Scroll-driven text animation with GSAP Flip and scramble effects. Elements morph smoothly while text dynamically reveals on scroll — ideal for cinematic, immersive landing sections.

Category
Page TransitionReact
CSS
Tailwind

Manual

Create a file and paste the following code into it.

src/components/ui/scroll-text-motion.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { useRef } from "react";
import {
  useScrollTextMotion,
  type ScrollTextGroup,
} from "@/hooks/use-scroll-text-motion";

interface ScrollTextMotionProps {
  groups: ScrollTextGroup[];
  logo?: string;
  className?: string;
}

export type { ScrollTextEl, ScrollTextGroup } from "@/hooks/use-scroll-text-motion";

export function ScrollTextMotion({
  groups,
  logo,
  className = "",
}: ScrollTextMotionProps) {
  const contentRef = useRef<HTMLDivElement>(null);
  const logoRef = useRef<HTMLSpanElement>(null);

  useScrollTextMotion(contentRef, logoRef, groups, logo);

  return (
    <div
      className={`scroll-text-motion min-h-dvh bg-black text-white ${className}`}
    >
      {logo && (
        <div className="scroll-text-fixed fixed inset-0 grid place-items-center pointer-events-none z-0">
          <div className="scroll-text-logo text-[clamp(2rem,10vw,4rem)] font-[lores-12] font-normal">
            <span ref={logoRef}>{logo}</span>
          </div>
        </div>
      )}

      <div
        className="scroll-text-content pt-[100vh] pb-[25vh] px-6 relative z-10"
        ref={contentRef}
      >
        {groups.map((group, gi) => (
          <div key={gi} className="scroll-text-group flex flex-col mb-[10vh]">
            {group.items.map((item, ii) => (
              <div
                key={ii}
                className={`scroll-text-el el whitespace-nowrap uppercase ${
                  item.pos
                } ${item.xl ? "el--xl" : ""} ${
                  item.typingIndicator ? "typing-indicator" : ""
                }`}
                data-alt-pos={item.altPos}
                data-flip-ease={item.flipEase ?? "expo.inOut"}
                data-scramble-duration={String(item.scrambleDuration ?? 1)}
              >
                <span className="scroll-text-inner">{item.text}</span>
              </div>
            ))}
          </div>
        ))}
      </div>
    </div>
  );
}

Update the import paths to match your project setup.