Skip to content
animorio
IT
Back to the library

The flatplan transition

TransitionsGSAP, CSS onlyMediumCompressed weight: Demo 7.3 KBGSAP 28 KBFreeDownload the zip

A page change in the minimal style: on a click the page steps back until the whole site shows in a row like a magazine's flatplan, with hairline frames and the page names on a rule. Two pairs of red crop marks run to the chosen page, stretching and springing shut around it, the row brings it to the middle and the view walks back in.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The flatplan transition</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <!-- the whole site laid out in a row, like the pages of a magazine pinned up on the wall: the camera moves over it -->
    <div class="track" data-track>
      <section class="page page--index" data-page aria-label="Index">
        <header class="bar">
          <a class="brand" href="#index" data-go="0">Marlow</a>
          <nav class="nav" aria-label="Pages">
            <a href="#index" data-go="0" aria-current="page">Index</a>
            <a href="#work" data-go="1">Work</a>
            <a href="#studio" data-go="2">Studio</a>
            <a href="#contact" data-go="3">Contact</a>
          </nav>
        </header>
        <div class="body">
          <p class="kicker">Furniture, rooms and type</p>
          <h1 class="title" data-title>Quiet work.</h1>
          <p class="lede">A small studio that makes a few good things each year, and keeps them going for a long time.</p>
        </div>
        <footer class="foot">
          <span class="note">Since 2014</span>
          <a class="next" href="#work" data-go="1" data-autoclic>
            <span class="next__text">Next, Work</span>
            <span class="next__arrow" aria-hidden="true"><span class="next__shaft"></span><svg class="next__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
          </a>
        </footer>
      </section>

      <section class="page page--work" data-page aria-label="Work">
        <header class="bar">
          <a class="brand" href="#index" data-go="0">Marlow</a>
          <nav class="nav" aria-label="Pages">
            <a href="#index" data-go="0">Index</a>
            <a href="#work" data-go="1" aria-current="page">Work</a>
            <a href="#studio" data-go="2">Studio</a>
            <a href="#contact" data-go="3">Contact</a>
          </nav>
        </header>
        <div class="body">
          <h2 class="title" data-title>Work</h2>
          <ul class="list">
            <li><span>Harbour chair</span><span class="list__kind">Oak, wool</span><span>2025</span></li>
            <li><span>Linden lamp</span><span class="list__kind">Brass, paper</span><span>2024</span></li>
            <li><span>Atlas shelf</span><span class="list__kind">Ash</span><span>2023</span></li>
          </ul>
        </div>
        <footer class="foot">
          <span class="note">Three of forty</span>
          <a class="next" href="#studio" data-go="2" data-autoclic>
            <span class="next__text">Next, Studio</span>
            <span class="next__arrow" aria-hidden="true"><span class="next__shaft"></span><svg class="next__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
          </a>
        </footer>
      </section>

      <section class="page page--studio" data-page aria-label="Studio">
        <header class="bar">
          <a class="brand" href="#index" data-go="0">Marlow</a>
          <nav class="nav" aria-label="Pages">
            <a href="#index" data-go="0">Index</a>
            <a href="#work" data-go="1">Work</a>
            <a href="#studio" data-go="2" aria-current="page">Studio</a>
            <a href="#contact" data-go="3">Contact</a>
          </nav>
        </header>
        <div class="body">
          <h2 class="title" data-title>Studio</h2>
          <div class="cols">
            <p>Four people around one long table, a workshop downstairs and a window onto the canal.</p>
            <p>We draw everything by hand first, then build it full size, then sit on it for a week.</p>
          </div>
        </div>
        <footer class="foot">
          <span class="note">Visits by appointment</span>
          <a class="next" href="#contact" data-go="3" data-autoclic>
            <span class="next__text">Next, Contact</span>
            <span class="next__arrow" aria-hidden="true"><span class="next__shaft"></span><svg class="next__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
          </a>
        </footer>
      </section>

      <section class="page page--contact" data-page aria-label="Contact">
        <header class="bar">
          <a class="brand" href="#index" data-go="0">Marlow</a>
          <nav class="nav" aria-label="Pages">
            <a href="#index" data-go="0">Index</a>
            <a href="#work" data-go="1">Work</a>
            <a href="#studio" data-go="2">Studio</a>
            <a href="#contact" data-go="3" aria-current="page">Contact</a>
          </nav>
        </header>
        <div class="body">
          <h2 class="title" data-title>Say hello.</h2>
          <p class="lede">Monday to Friday, nine to six. Studio 4, Canal Yard.</p>
        </div>
        <footer class="foot">
          <span class="note">Write any time</span>
          <a class="next" href="#index" data-go="0" data-autoclic>
            <span class="next__text">Back to the start</span>
            <span class="next__arrow" aria-hidden="true"><span class="next__shaft"></span><svg class="next__head" viewBox="0 0 8 12"><path d="M1.5 1.5 6.5 6l-5 4.5"/></svg></span>
          </a>
        </footer>
      </section>
    </div>

    <!-- the flatplan: hairline frames, the rule with the page names, the crop marks (all placed by the script) -->
    <div class="plan" data-plan aria-hidden="true">
      <div class="rule" data-rule><span class="rule__line"></span></div>
      <div class="crop" data-crop>
        <span class="crop__c crop__c--tl"><i></i></span>
        <span class="crop__c crop__c--tr"><i></i></span>
        <span class="crop__c crop__c--bl"><i></i></span>
        <span class="crop__c crop__c--br"><i></i></span>
      </div>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

62 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The flatplan transition” from Animorio and fit it into my site

What it does

The site of a made-up furniture studio, Marlow, in four pages and almost nothing else: Index, Work, Studio and Contact, each with a big headline in condensed Archivo, a line of text and the link to the next page. On load the site is seen from above like a flatplan: the pages in a row on the warm grey ground, a hairline under them drawing out from the middle, ticks at the edges of every page, the names rising one after the other and four red crop marks drawing themselves at the corners of the first one. Then the view walks in and the headline feels the landing: the letters dip a hair one after the other and spring back. A click on Next, Work (or on a menu item) sends the page away, far enough to see both itself and the page you asked for, looking halfway between the two. The crop marks run: the front pair leaves first and the back pair follows a beat later, so the frame stretches over the pages in between and then springs shut around the new one with a little bounce; below, the new page's name gains weight and widens. The row slides until the chosen page is in the middle while the view walks back in, stops a hair past full size and settles. From the last page, Back to the start goes back to the first across the whole site: the view steps further back and the crop marks run the whole row.

How it works

The site in a row

The four pages are real, exactly as big as the screen, and sit side by side inside a single container, GAP times their width apart. The script never moves the pages: it moves the view. The view is two numbers, cx (the page in the middle, which can be 1.4, between two pages) and s (the zoom), and those two numbers make one transform on the container: a translate that puts cx in the middle and scale(s) with the origin at the top left. Stepping back, sliding and walking in are three tweens on two numbers.

The flatplan on its own layer

The frames, the rule, the ticks, the names and the crop marks are not inside the pages: a one-pixel border shrunk to a third would turn into a third of a pixel and vanish. They live on a layer above, at true size, and on every frame the box(p) function says where page p falls with the view of that moment. The layer shows as much as the view is far: opacity from 0 at full size to 1 at SCALE.

Crop marks that stretch

The four crop marks follow two numbers, pl and pr: the page framed by the left pair and the page framed by the right pair. Going forward pr leaves first, with power3.inOut, and STRETCH seconds later pl follows, with back.out: for a moment the two pairs frame different pages and the frame stretches over the ones in between, then the back pair arrives, overshoots by a hair and closes. Going back the roles swap. Each arm draws itself from its corner with scale and back.out.

How far to step back

For a page next door SCALE is enough; for a far one the view steps back until both pages and the ones in between fit in 90 percent of the width, and looks halfway between the two. Then the row slides to the new page while the view is already walking in: the two tweens overlap and the move becomes a single curve. The walk in goes to 1.006 with power3.inOut and settles at 1 with power2.out; meanwhile the letters of the new headline dip 0.045em, one after the other, and spring back with elastic.out.

Direction, phone and reduced motion

Each change is one timeline, with timeScale for SPEED. A click that comes while the page is changing waits in a queue and goes as soon as the page fills the screen, without waiting for the last settle. Only the page on screen can be reached with Tab and with screen readers. On a phone the pages are tall and narrow and the flatplan is the same, smaller. With reduced motion nothing moves: the page fades out and the new one fades in already in place.

Values to change

NameFileDefaultWhat it changes
SCALEscript.js0.3How small the pages get in the flatplan: 0.3 is a third of the screen, 0.5 half. For far pages the view steps back further anyway.
GAPscript.js0.12The space between two pages, as a share of a page's width.
OUTscript.js0.7The seconds of the step back.
MOVEscript.js0.6The seconds the crop marks take to run to the next page (a little more for far ones).
STRETCHscript.js0.14How far the back pair of crop marks lags behind the front one, in seconds: 0 = the frame moves rigid.
INscript.js0.8The seconds of the walk back into the new page.
SPEEDscript.js1The speed of everything: 2 is twice as fast, 0.5 half as fast.

Try them in the Playground

When to use it

To move from one page to another on a site of a few pages where the whole matters: a designer's or a photographer's portfolio, an architect's studio, a magazine or a small publisher, the presentation of a collection. It shows at a glance where you are and where you're going, so it works best when the pages are few (three to six) and look different from each other.

On phones

The same transition with the sizes adapted: the pages are tall and narrow like the screen, the flatplan shows them smaller in a row, with the rule and the names below. The headlines fit the width and a tap on the link takes the trip.

With reduced motion

Nothing moves and nothing zooms: the flatplan doesn't show, the page fades out and the new one fades in already in place.

Pitfalls

  • From the last page to the first, with a fixed zoom, the front pair of crop marks ran off the screen and for a moment none could be seen: now the view steps back as far as it takes to see both pages and looks halfway between them.
  • Frames drawn as the pages' own border shrank with them and vanished: the whole flatplan lives on a layer at true size, put back in place on every frame.
  • The previews' autopilot clicks every 2.8 seconds and the trip from the last page to the first takes nearly as long: a click that comes before the end waits in a queue and goes as soon as the page fills the screen, cutting only the last settle.
  • The queue empties inside a callback of the finishing timeline, and the new timeline completes the old one with progress(1): calling it from inside that same timeline would cut it off halfway, so the queued click starts a beat later with delayedCall.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 7.3 KB; the libraries: GSAP 28 KB.

Testing

Tested on 14 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations