All components

Our Revolution Landing Page Reveal

GSAP animation component · Published 2026-07-27 · by vanguardia.dev

Open live demo ↗ Raw prompt (.md)

What it does

A preloader-to-hero landing reveal driven by a single GSAP timeline: a row of seven tall portrait images rises from below and staggers in, the whole strip slides horizontally, the side images and then the full black loader wipe away upward via clip-path, and finally the nav, hero headline and footer thumbnails slide up and fade in. Plays automatically once on page load using power3.inOut easing, about 6 seconds total.

How it's built

Categorypreloader
Techgsap
Complexitypage
Performance costlight
Mobile-safeyes

preloader reveal clip-path stagger timeline intro landing hero editorial

Rebuild it with AI

To reproduce this animation in your own project, copy the prompt below into Claude Code, Cursor or any AI coding agent. The prompt is validated — it describes the exact structure, timing and easing, so the agent rebuilds the effect faithfully and you can then adapt colors, copy and layout to your design.

The full prompt

Landing Page Reveal — Portrait-Strip Preloader Wipe-Up

Goal

Build a full-screen editorial landing hero with a cinematic preloader-to-hero reveal that plays automatically once on page load (~6.5 seconds total). A black full-screen loader holds a horizontal strip of seven tall portrait images (six photos plus a centered white monogram logo). On load the seven images rise up from below and stagger in, then the whole strip slides sideways; the six photos (but not the center logo) wipe away upward one after another via an animated clip-path; the entire black loader panel then wipes upward the same way, uncovering the page beneath; finally the nav links, the three-line hero headline, and the four footer thumbnails all slide up and fade in. Everything is driven by one single GSAP timeline using power3.inOut easing.

Tech

Vanilla HTML/CSS/JS with ES module imports. Use gsap (npm) only — no GSAP plugins and no smooth-scroll library (the page never scrolls; body is overflow:hidden). Import the default export (import gsap from "gsap") and fire the whole sequence inside a DOMContentLoaded listener.

Layout / HTML

Semantic structure (class names / the #loader-logo id are load-bearing — the JS/CSS query them):

<div class="container">

  <!-- fixed black preloader on top of everything -->
  <div class="loader">
    <div class="loader-imgs">
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 1 -->
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 2 -->
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 3 -->
      <div class="img" id="loader-logo"><img src="logo.png" alt="" /></div>  <!-- CENTER logo, survives the wipe -->
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 4 -->
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 5 -->
      <div class="img"><img src="..." alt="" /></div>          <!-- photo 6 -->
    </div>
  </div>

  <!-- the page underneath, revealed when the loader wipes away -->
  <div class="website-content">
    <nav>
      <div class="nav-item"><a href="#">( menu )</a></div>
      <div class="nav-item" id="logo"><a href="#">EliteWebsites</a></div>
      <div class="nav-item"><a href="#">( work* )</a></div>
    </nav>

    <div class="hero">
      <div class="h1"><h1>we believe</h1></div>
      <div class="h1"><h1>websites shape the</h1></div>
      <div class="h1"><h1><span>future</span></h1></div>
    </div>

    <footer>
      <div class="item"><img src="..." alt="" /></div>
      <div class="item"><img src="..." alt="" /></div>
      <div class="item"><img src="..." alt="" /></div>
      <div class="item"><img src="..." alt="" /></div>
    </footer>
  </div>

</div>

Notes:

  • .loader-imgs holds exactly 7 .img wrappers. The 4th one (the center) carries id="loader-logo" and holds the monogram logo — it is the one image that is excluded from the photo wipe and stays visible until the whole loader panel wipes away.
  • .website-content holds the nav, the hero, and the footer, and sits underneath the loader.
  • Use "EliteWebsites" as the neutral placeholder brand name in the center nav slot. The three headline lines are the tagline "we believe / websites shape the / future" — the word "future" is wrapped in a <span> so it can be styled bolder.

Styling

Fonts: the CSS declares custom display faces — "Sharp Grotesk" (body + the future span), "Neue Montreal" (nav), "PP Editorial Old" (center nav brand), "PP Migra" (hero headline). None of these are bundled or loaded (there is no @font-face and no font <link>), so in practice everything falls back to the browser default sans-serif. Keep the declared font-family names for fidelity but do not attempt to fetch the missing fonts.

Palette:

  • Page background: #e7e4e5 (warm light grey).
  • Loader background: #000 (black).
  • All text: #000.

Global:

  • * { margin:0; padding:0; box-sizing:border-box }.
  • html, body { font-family:"Sharp Grotesk"; background:#e7e4e5; overflow:hidden } (no scrolling).
  • img { width:100%; height:100%; object-fit:cover }.
  • a { text-decoration:none; text-transform:uppercase; color:#000 }.

Key elements (positioning + the clip-paths and initial states the animation depends on):

  • .loader: position:fixed; width:100vw; height:100vh; background:#000; pointer-events:none. Initial clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%) (full rectangle — covers the whole viewport). This exact 4-point full-rect clip-path is required so GSAP can later animate it to a collapsed polygon.
  • .loader-imgs: width:150%; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); display:flex; gap:50px; same full-rect clip-path. It is 150% wide, centered, so the strip overflows the viewport on both sides.
  • .img: position:relative; flex:1 (seven equal columns filling the 150% strip); same full-rect clip-path on each.
  • nav: width:100%; padding:30px; display:flex; align-items:center; font-family:"Neue Montreal".
  • .nav-item: position:relative; flex:1 (three equal thirds). .nav-item:nth-child(2) (the brand): text-align:center; font-family:"PP Editorial Old"; font-size:24px; font-weight:500. .nav-item:nth-child(3): text-align:right. #logo a { text-transform:none } (brand name not uppercased).
  • .hero: width:100%; position:absolute; top:45%; transform:translateY(-50%); text-transform:uppercase; font-family:"PP Migra".
  • .h1: width:100%; text-align:center; font-size:4vw; same full-rect clip-path on each line.
  • h1: position:relative; font-weight:400; line-height:85%.
  • h1 span: font-family:"Sharp Grotesk"; font-weight:700 (the word "future" renders heavier).
  • footer: width:100%; position:absolute; bottom:30px; right:30px; display:flex; justify-content:flex-end; gap:10px; same full-rect clip-path.
  • .item: position:relative; width:100px; height:100px (four square thumbnails, bottom-right, object-fit:cover).

GSAP effect (be exact)

Initial states — set BEFORE building the timeline
gsap.set(".img",        { y: 500 });            // all 7 strip images pushed 500px DOWN
gsap.set(".loader-imgs",{ x: 500 });            // whole strip shifted 500px to the RIGHT
gsap.set(".nav-item",   { y: 25, opacity: 0 }); // nav items 25px down, invisible
gsap.set("h1, .item, footer", { y: 200 });      // hero headlines, footer thumbs AND the footer container 200px down

(The full-rect clip-path initial states come from the CSS above, not from gsap.set.)

One single timeline, delayed 1s
const tl = gsap.timeline({ delay: 1 });

The whole sequence waits 1s after DOMContentLoaded, then plays as a chain of five tweens. Position parameters are relative offsets ("-=X" = start X seconds before the current end of the timeline) — reproduce them exactly.

Tween 1 — images rise (starts at timeline t=0):

tl.to(".img", {
  y: 0,
  duration: 1.5,
  stagger: 0.05,
  ease: "power3.inOut",
});

All 7 .img wrappers rise from y:500 to y:0, left-to-right stagger 0.05s, each 1.5s, power3.inOut. (Strip fills from t≈0 to t≈1.8.)

Tween 2 — strip slides sideways (position "-=2.5"):

tl.to(".loader-imgs", {
  x: 0,
  duration: 3,
  ease: "power3.inOut",
}, "-=2.5");

The whole strip slides from x:500 to x:0 (moves left ~500px) over 3s. Position "-=2.5" computes to a negative insertion point (GSAP clamps it to 0), so this runs in parallel with the rising images, starting at t≈0 and ending at t≈3.

Tween 3 — the six photos wipe away upward (position "-=1"):

tl.to(".img:not(#loader-logo)", {
  clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)",
  duration: 1,
  stagger: 0.1,
  ease: "power3.inOut",
}, "-=1");

Every .img except the center #loader-logo (i.e. the 6 photos) animates its clip-path from the full rectangle to polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%) — the bottom edge collapses up to the top edge, so each photo wipes away upward, staggered 0.1s, 1s each. Starts at t≈2. The center logo stays visible on the black panel.

Tween 4 — the whole black loader wipes upward (position "-=0.5"):

tl.to(".loader", {
  clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)",
  duration: 1,
  ease: "power3.inOut",
}, "-=0.5");

The entire black .loader panel (still carrying the center logo) collapses its bottom edge up to the top the same way — wiping the whole preloader upward off the screen over 1s, uncovering .website-content beneath. Starts at t≈3.

Tween 5 — page content slides up + fades in (position "-=0.5"):

tl.to(".nav-item, h1, footer, .item", {
  y: 0,
  opacity: 1,
  stagger: 0.1,
  duration: 1,
  ease: "power3.inOut",
}, "-=0.5");

All nav items (3), all hero headline h1s (3), the footer container, and the footer thumbnails (4) — 11 targets total — slide from their offset (y:200, or y:25 for nav) to y:0 and fade opacity 0→1 (only the nav items were transparent), staggered 0.1s, 1s each. Starts at t≈3.5, ends at t≈5.5.

Timeline summary (timeline-relative seconds; add the 1s delay for wall-clock)

| t (s) | what | |-------|------| | 0.0–1.8 | 7 strip images rise y:500→0 (stagger 0.05, dur 1.5) | | 0.0–3.0 | strip slides x:500→0 (dur 3) — parallel | | 2.0–3.5 | 6 photos wipe upward via clip-path (stagger 0.1, dur 1) | | 3.0–4.0 | whole black loader wipes upward via clip-path (dur 1) | | 3.5–5.5 | nav + hero h1s + footer + thumbs slide up & fade in (stagger 0.1, dur 1) |

Total runtime ≈ 6.5s (including the 1s start delay). Every tween uses ease: "power3.inOut".

Ease reference
  • Everything uses power3.inOut. There is no CustomEase, SplitText, ScrollTrigger, lerp loop, or Three.js in this component — it is a pure load-triggered timeline of y, x, clip-path and opacity tweens.

Assets / images

Thirteen image slots total: 6 tall portrait photos + 1 portrait logo in the loader strip, and 4 square thumbnails in the footer.

  • Loader strip photos (6) — tall portrait ~2:3 editorial/cinematic frames, object-fit:cover, each a single subject with a moody, atmospheric palette (mixed teals, golds, greens). Roughly, in strip order: (1) a lone figure walking a wet reflective floor toward a shaft of light between dark angular walls; (2) extreme close-up of a glossy helmet with a swirled gold visor on lime-green; (3) a spacesuited figure before a huge pale sphere in hazy golden light; then the center logo; (4) a dark slit-view of a lower face holding a lit cigarette; (5) an orange-lit silhouette pressing a hand on frosted glass; (6) a side-lit portrait of a woman in dark clothing on a dark teal backdrop. They are only briefly seen before wiping away, so the palettes need not match.
  • Center logo (#loader-logo) — an ornate white cursive monogram letter on a solid black background, portrait ~3:4. This is the one image that stays visible while the six photos wipe, then leaves with the black panel. Keep it on a black field so it blends into the loader.
  • Footer thumbnails (4) — small square images (cropped to 100×100 via object-fit:cover), bottom-right. Any editorial/3D-render/portrait subjects work (e.g. a surreal metallic-faced 3D figure, a golden lucky-cat figurine in a red niche, a veiled portrait, a warm hazy city skyline). Do not use real brand marks.

If you have fewer fixtures than slots, repeat images to fill; if more, use the first ones.

Behavior notes

  • Autoplay once on DOMContentLoaded; no scroll, hover, or click triggers. The page never scrolls (overflow:hidden).
  • The clip-path target polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%) is the "wipe upward" shape (bottom edge collapsed to the top). The full-rectangle initial clip-paths on .loader, .loader-imgs, .img, .h1 and footer must be present in CSS so GSAP can tween between matching 4-point polygons.
  • No will-change hints, no reduced-motion handling, and no responsive breakpoints in the original — it renders the same at all widths (the loader strip is 150% wide and centered, so it always overflows). Layout is light and mobile-safe.