TweenLabs LogoTweenLabs
TweenLabs/Components/Source Code

Scroll Tags Code

Endpoint: /02-scroll-tags-assembly

Interactive scroll-triggered tags that fly into a grid board container from all offscreen directions.

šŸ“¦ GSAP: ^3.15.0
šŸ“¦ @gsap/react: ^2.1.2
āš™ļø ScrollTrigger: āœ… Required
page.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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
"use client";

import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useRef } from "react";

gsap.registerPlugin(useGSAP, ScrollTrigger);

const tagsData = [
  {
    text: "CREATIVE",
    color: "bg-wtf-orange text-white",
    xStart: -400,
    yStart: -200,
    rotate: -15,
  },
  {
    text: "DEVELOPER",
    color: "bg-wtf-green text-white",
    xStart: 400,
    yStart: 250,
    rotate: 12,
  },
  {
    text: "GSAP 3",
    color: "bg-wtf-yellow text-black",
    xStart: -300,
    yStart: 300,
    rotate: -8,
  },
  {
    text: "NEXT.JS 16",
    color: "bg-wtf-purple text-white",
    xStart: 500,
    yStart: -150,
    rotate: 15,
  },
  {
    text: "REACT 19",
    color: "bg-wtf-blue text-white",
    xStart: -500,
    yStart: 100,
    rotate: 5,
  },
  {
    text: "BRUTALIST",
    color: "bg-wtf-red text-white",
    xStart: 300,
    yStart: -300,
    rotate: -12,
  },
  {
    text: "SCROLL TRIGGER",
    color: "bg-black text-white",
    xStart: -600,
    yStart: -100,
    rotate: 20,
  },
  {
    text: "FLUID MOTION",
    color: "bg-white text-black",
    xStart: 600,
    yStart: 400,
    rotate: -6,
  },
];

export default function AnimationTwoPage() {
  const containerRef = useRef<HTMLDivElement>(null);
  const scrollSectionRef = useRef<HTMLDivElement>(null);

  useGSAP(
    () => {
      const tags = gsap.utils.toArray<HTMLElement>(".assembler-tag");

      const scroller = containerRef.current?.closest("#main-scroller") || undefined;

      // Pin the scroll section and animate the tags entering the DOM
      const tl = gsap.timeline({
        scrollTrigger: {
          trigger: scrollSectionRef.current,
          scroller: scroller,
          start: "top top",
          end: "+=2000",
          pin: true,
          scrub: 1,
          anticipatePin: 1,
        },
      });

      tags.forEach((tag, idx) => {
        const xStart = Number(tag.getAttribute("data-xs") || 0);
        const yStart = Number(tag.getAttribute("data-ys") || 0);
        const rotate = Number(tag.getAttribute("data-rot") || 0);

        // Animate each tag flying into its DOM grid cell from offscreen
        tl.fromTo(
          tag,
          {
            x: xStart,
            y: yStart,
            rotation: rotate * 3,
            opacity: 0,
            scale: 0.2,
          },
          {
            x: 0,
            y: 0,
            rotation: rotate,
            opacity: 1,
            scale: 1,
            ease: "power2.out",
          },
          idx * 0.15, // stagger offset in timeline
        );
      });
    },
    { scope: containerRef },
  );

  return (
    <div
      className="relative bg-[#f0eadf] text-[#2a2a2a] selection:bg-wtf-yellow selection:text-black"
      ref={containerRef}
    >
      <div className="absolute inset-0 dot-grid pointer-events-none z-0" />

      {/* Floating Back Button */}
      <div className="fixed left-6 top-6 z-50 pointer-events-auto">
        <button
          onClick={() =>
            window.history.length > 1
              ? window.history.back()
              : (window.location.href = "/")
          }
          className="brutalist-btn bg-wtf-yellow text-xs font-mono font-bold py-2.5 px-4 rounded-md uppercase cursor-pointer"
        >
          ← Back
        </button>
      </div>

      {/* Intro section */}
      <section className="h-[70vh] flex flex-col items-center justify-center text-center px-4 gap-4 z-10 relative">
        <div className="inline-flex items-center gap-2 bg-wtf-green border-2 border-[#2a2a2a] px-4 py-1.5 rounded-full text-[10px] font-mono font-bold text-white uppercase tracking-widest shadow-[3px_3px_0px_#2a2a2a] tilt-right">
          <span>Scroll Tags Assembly Sandbox</span>
        </div>
        <h1 className="text-4xl md:text-6xl font-serif font-black uppercase max-w-2xl leading-[1.1]">
          Scroll Down to Assemble the Tags
        </h1>
        <p className="font-mono text-xs text-zinc-500 uppercase tracking-widest animate-bounce mt-4">
          ↓ Scroll Down ↓
        </p>
      </section>

      {/* Assembly ScrollTrigger Section */}
      <section
        ref={scrollSectionRef}
        className="h-[calc(100vh-64px)] w-full flex items-center justify-center relative overflow-hidden bg-white border-y-3 border-[#2a2a2a]"
      >
        <div className="absolute inset-0 dot-grid opacity-5 pointer-events-none" />

        <div className="z-10 w-full max-w-4xl px-4 flex flex-col gap-8 items-center">
          <div className="font-mono text-xs font-bold text-zinc-400 uppercase tracking-widest">
            DOM Board Collider
          </div>

          {/* Target grid container where tags land */}
          <div className="w-full min-h-[300px] border-3 border-[#2a2a2a] rounded-xl bg-zinc-50 p-8 flex flex-wrap gap-4 items-center justify-center shadow-[inset_4px_4px_10px_rgba(0,0,0,0.05)] relative">
            {tagsData.map((tag, i) => (
              <span
                key={i}
                className={`assembler-tag px-6 py-3 border-2 border-[#2a2a2a] rounded-lg font-mono font-black text-sm md:text-base shadow-[3px_3px_0px_#2a2a2a] transform will-change-transform ${tag.color}`}
                data-xs={tag.xStart}
                data-ys={tag.yStart}
                data-rot={tag.rotate}
              >
                {tag.text}
              </span>
            ))}
          </div>
        </div>
      </section>

    </div>
  );
}
master*0 ā“§0 ⚠
Ln 188, Col 1Spaces: 2UTF-8TypeScript JSXPrettier

āš™ļø Setup & Integration Guide

How to install, import, and configure this animation in your project

šŸ’»

Option A: Install via CLI (Recommended)

You can install this component directly into your project via the TweenLabs CLI. It automatically creates the file and configures dependencies:

npx tweenlabs add scroll-tags-assembly

Option B: Manual Installation

Follow these steps to integrate the component into your project manually:

1

Install Packages

First, install GSAP and its official React hook helper library (@gsap/react).

npm install gsap @gsap/react
2

Add Required CSS Styles

Copy the styles from the Required CSS tab above, or open the styles.css file that was automatically downloaded with your component. Paste these classes into your global stylesheet (e.g. src/app/globals.css or similar).

3

Create Component File

Create a new file in your React or Next.js project (e.g. src/components/ScrollTags.tsx) and paste the code from the Standalone React Component tab above. If no standalone tab is available, copy the full page file code and adjust the routing logic for your needs.

āš ļø ScrollTrigger Plugin Notice

This component uses scroll-triggered timing events. Make sure to register the plugin as shown inside the code:

GSAP Registration
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(useGSAP, ScrollTrigger);
4

Import & Render

Import and render the component in your page or view layout:

App Page
import ScrollTags from "@/components/ScrollTags";

export default function Page() {
  return (
    <main className="min-h-screen p-8 bg-[#f5f5f5] flex items-center justify-center">
      <ScrollTags />
    </main>
  );
}
šŸ’”

Customization & Component Properties

šŸ› ļø Customization & Component Properties (Props)

You can pass the following settings to configure the layout and animation details:

  • tags (Array): An array of tag objects. Each object contains text (string), color (Tailwind class), xStart (number), yStart (number), and rotate (number).