SectionsIntermediateApril 8, 2026

Full Orchestrated Load Sequence

A complete timed entrance sequence: loading screen holds for 2.5s, wipes away, then all page elements (headline, subtext, nav items) stagger in with coordinated timing via a GSAP timeline.

View Full Demo →

Digital experiences that move people.

We build motion-forward websites for studios, founders, and brands who want to stand out.

Loading
index.jsx
"use client";

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

export default function FullOrchestratedLoadSequence({ siteName = "", headline = "", subtext = "", navLinks = [] }) {
  const [isPlaying, setIsPlaying] = useState(false);
  const [isDone, setIsDone] = useState(false);
  const loaderRef = useRef(null);
  const navRef = useRef(null);
  const headlineRef = useRef(null);
  const subtextRef = useRef(null);

  function play() {
    if (isPlaying) return;
    setIsPlaying(true);
    setIsDone(false);

    // Reset all elements
    gsap.set(loaderRef.current, { yPercent: 0 });
    gsap.set(navRef.current?.children ?? [], { opacity: 0, y: 10 });
    gsap.set(headlineRef.current, { opacity: 0, y: 40 });
    gsap.set(subtextRef.current, { opacity: 0, y: 20 });

    const tl = gsap.timeline({
      onComplete: () => { setIsPlaying(false); setIsDone(true); },
    });

    // 1. Loading screen holds for 2.5s then wipes up
    tl.to(loaderRef.current, {
      yPercent: -100,
      duration: 0.8,
      ease: "power3.inOut",
      delay: 2.5,
    });

    // 2. Headline slides up
    tl.to(headlineRef.current, {
      opacity: 1,
      y: 0,
      duration: 0.8,
      ease: [0.16, 1, 0.3, 1],
    }, "-=0.3");

    // 3. Subtext fades in
    tl.to(subtextRef.current, {
      opacity: 1,
      y: 0,
      duration: 0.7,
      ease: [0.16, 1, 0.3, 1],
    }, "-=0.5");

    // 4. Nav items stagger in
    tl.to(navRef.current?.children ?? [], {
      opacity: 1,
      y: 0,
      stagger: 0.07,
      duration: 0.6,
      ease: [0.16, 1, 0.3, 1],
    }, "-=0.4");
  }

  return (
    <div className={styles.demo}>
      {/* Page content revealed after load */}
      <div className={styles.page}>
        <nav ref={navRef} className={styles.nav}>
          <span>{siteName}</span>
          {navLinks.map((link) => (
            <a key={link.label} href={link.href} className={styles.navLink}>
              {link.label}
            </a>
          ))}
        </nav>
        <div className={styles.hero}>
          <h1 ref={headlineRef} className={styles.headline}>{headline}</h1>
          <p ref={subtextRef} className={styles.subtext}>{subtext}</p>
        </div>
      </div>

      {/* Loading screen overlay */}
      <div ref={loaderRef} className={styles.loader}>
        <span className={styles.loaderText}>Loading</span>
      </div>

      {/* Controls */}
      {!isPlaying && (
        <button className={styles.playBtn} onClick={play}>
          {isDone ? "Replay" : "Play Sequence"}
        </button>
      )}
    </div>
  );
}
styles.module.css
.demo {
  position: relative;
  height: 420px;
  overflow: hidden;
}

.page {
  position: absolute;
  inset: 0;
  background: #0d0d0d;
  display: flex;
  flex-direction: column;
}

.nav {
  display: flex;
  align-items: center;
  gap: 2rem;
  padding: 1.5rem 2rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.nav > span {
  font-size: 0.9375rem;
  font-weight: 600;
  color: #ffffff;
  letter-spacing: -0.01em;
  margin-right: auto;
}

.navLink {
  font-size: 0.875rem;
  color: rgba(255, 255, 255, 0.5);
  text-decoration: none;
  letter-spacing: -0.01em;
}

.hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 2rem;
  gap: 1rem;
}

.headline {
  font-size: clamp(1.75rem, 5vw, 3rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  color: #ffffff;
  line-height: 1.05;
}

.subtext {
  font-size: 0.9375rem;
  color: rgba(255, 255, 255, 0.45);
  max-width: 40ch;
  line-height: 1.65;
}

.loader {
  position: absolute;
  inset: 0;
  background: #1a1a1a;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.loaderText {
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.3);
}

.playBtn {
  position: absolute;
  bottom: 1.25rem;
  right: 1.25rem;
  z-index: 20;
  padding: 0.625rem 1.25rem;
  font-size: 0.8125rem;
  font-weight: 500;
  font-family: inherit;
  color: #1a1a1a;
  background: #ffffff;
  border: none;
  border-radius: 9999px;
  cursor: pointer;
}
  • gsap

May 11, 2026

SECTIONS

Dual Push Cards

Two-up CTA card section with scroll-driven parallax on each card's background image. Cards scale down and drift vertically as you scroll past. Glassmorphic blur buttons at bottom-left. Stacks on mobile, side-by-side grid on desktop.

May 4, 2026

SECTIONS

Portfolio Grid

A responsive portfolio showcase section with a header tagline, blinking cursor counters, a 2-up/4-col project card grid with hover-zoom images and data-label metadata, plus a full-width CTA button. Scroll-triggered fade and move-up animations via GSAP.

May 1, 2026

SECTIONS

Logo Wall Cycle

A responsive logo grid that cycles through brand logos with smooth GSAP-powered swap animations. Shows 8 logos on desktop and 6 on tablet, shuffling hidden logos into view on a timed loop. Pauses when out of viewport or tab is hidden.