AnimationsSimpleApril 8, 2026

Scale Up Transition

Page elements scale up from 0.8 and fade in on enter, giving a zoom-in feel on arrival. Exit is a shared opacity fade. gsap.fromTo with stagger creates sequential reveal.

View Full Demo →

Home

  • Featured Work
  • Latest Projects
  • About Studio
index.jsx
"use client";

import { useEffect, useRef, useState } from "react";
import gsap from "gsap";
import styles from "./styles.module.css";

const PAGES = [
  { label: "Home", bg: "#f5f5f5", color: "#1a1a1a", items: ["Featured Work", "Latest Projects", "About Studio"] },
  { label: "Projects", bg: "#111111", color: "#ffffff", items: ["Branding", "Web", "Motion"] },
  { label: "Contact", bg: "#1a2a1a", color: "#ffffff", items: ["Email", "LinkedIn", "Instagram"] },
];

export default function ScaleUpTransition() {
  const [pageIndex, setPageIndex] = useState(0);
  const [displayIndex, setDisplayIndex] = useState(0);
  const containerRef = useRef(null);
  const isAnimating = useRef(false);

  function navigate(nextIndex) {
    if (isAnimating.current || nextIndex === pageIndex) return;
    isAnimating.current = true;

    const container = containerRef.current;
    gsap.to(container, {
      opacity: 0,
      duration: 0.3,
      ease: "power1.in",
      onComplete: () => {
        setDisplayIndex(nextIndex);
        setPageIndex(nextIndex);
      },
    });
  }

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const items = container.querySelectorAll(`.${styles.item}`);
    if (!items.length) return;

    // Enter: scale up from 0.8
    gsap.fromTo(
      items,
      { opacity: 0, scale: 0.8 },
      {
        opacity: 1,
        scale: 1,
        stagger: 0.1,
        duration: 0.5,
        ease: "power2.out",
        onStart: () => gsap.set(container, { opacity: 1 }),
        onComplete: () => { isAnimating.current = false; },
      }
    );
  }, [displayIndex]);

  const page = PAGES[displayIndex];

  return (
    <div className={styles.demo}>
      <div
        ref={containerRef}
        className={styles.page}
        style={{ background: page.bg, color: page.color }}
      >
        <h2 className={styles.pageTitle}>{page.label}</h2>
        <ul className={styles.list}>
          {page.items.map((item) => (
            <li key={item} className={styles.item}>{item}</li>
          ))}
        </ul>
      </div>
      <div className={styles.nav}>
        {PAGES.map((p, i) => (
          <button
            key={p.label}
            className={`${styles.navBtn} ${i === pageIndex ? styles.active : ""}`}
            onClick={() => navigate(i)}
          >
            {p.label}
          </button>
        ))}
      </div>
    </div>
  );
}
styles.module.css
.demo {
  display: flex;
  flex-direction: column;
}

.page {
  height: 320px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  transition: background 0ms;
}

.pageTitle {
  font-size: 2.5rem;
  font-weight: 700;
  letter-spacing: -0.03em;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: center;
}

.item {
  font-size: 0.9375rem;
  opacity: 0.65;
}

.nav {
  display: flex;
  gap: 1px;
  background: #e5e5e5;
  border-top: 1px solid #e5e5e5;
}

.navBtn {
  flex: 1;
  padding: 0.875rem;
  font-size: 0.875rem;
  font-weight: 500;
  font-family: inherit;
  background: #ffffff;
  border: none;
  cursor: pointer;
  color: #6b6b6b;
  transition: background 150ms ease, color 150ms ease;
}

.navBtn:hover {
  background: #f5f5f5;
  color: #1a1a1a;
}

.navBtn.active {
  background: #1a1a1a;
  color: #ffffff;
}
  • gsap

Apr 27, 2026

ANIMATIONS

Cursor Hover Label

A custom cursor label that follows the mouse and fades in when hovering trigger elements. Uses GSAP quickTo for smooth tracking. Trigger elements use data-cursor-label attributes to set label text. Inspired by portfolio/agency sites like Studio PIC.

Apr 27, 2026

ANIMATIONS

Section Transition 01

GSAP ScrollTrigger-based section transition system. Sibling sections opt into parallax, pin, or reveal modes via data attributes. Supports y offset, overlay opacity, and overlay color per section. Mobile strategy simplifies motion on smaller screens.

Apr 27, 2026

ANIMATIONS

Text Reveal 01

GSAP SplitText-based text reveal system. Text elements opt in with data-reveal-01 and split into lines, words, or characters. Supports load-time reveals, scroll-triggered reveals, scrubbed scroll reveals, and manual split-only mode for custom timelines. Per-element overrides for duration, stagger, delay, ease, and replay behavior.