Targ Apps Docs
Targ Apps Site

Animations & GSAP

How scroll effects, hero animations, and ScrollTrigger initialization work on the marketing site.

The home page uses GSAP for hero entrance animations, scroll-triggered reveals, and the pinned section stack. Effects must measure layout after fonts and images settle — especially on the first prerendered load.

Initialization

Plugin registration happens once via app/lib/gsap.ts (imported from app/root.tsx):

  • ScrollTrigger and @gsap/react useGSAP are registered before any component mounts.
  • waitForLayout() in app/lib/wait-for-layout.ts waits for document.fonts.ready, decodes eager or already-loaded images in scope (skips loading="lazy" images that have not started fetching — they would deadlock img.decode() and block pin init), then double-requestAnimationFrame before measuring. Late lazy loads are handled by image load listeners in PinnedSectionStack.
  • scheduleScrollTriggerRefresh() runs ScrollTrigger.refresh() after layout settle, window.load, and fonts — used by SmoothScroll.
  • notifyGsapLayoutReady() dispatches targ:gsap-layout-ready after the pinned stack finishes pin setup so scroll reveals can refresh.

Scroll reveals

useScrollReveal (app/hooks/useScrollReveal.ts) scopes data-reveal-group / data-reveal queries to the hook’s ref (not the whole document), waits for layout, creates ScrollTriggers, then refreshes and plays any triggers already in view.

Listen for targ:gsap-layout-ready or call refreshScrollTrigger() + playActiveScrollTriggers() after layout-changing work (e.g. pin stack init).

Pinned section stack

PinnedSectionStack defers pin timeline creation until waitForLayout on the stack root, then calls notifyGsapLayoutReady() when pin metrics are applied. Below lg (1024px) it skips pin/fake-scroll and still notifies layout ready.

Manual verification

  1. Hard refresh (empty cache) on / — hero wordmark sweep and tagline/marquee should animate without a second load.
  2. Scroll through pinned sections — reveals should fire on first visit; pin scrub should stay aligned with scroll.
  3. Resize across the lg breakpoint — pin should enable/disable without stale triggers.
  4. Enable prefers-reduced-motion — content should appear without motion, no invisible stuck elements.

On this page