Skip to content
animorio
IT
All components

The menu under the page

Menus, 3D, Physics, TransitionsGSAP, CanvasAdvancedCompressed weight: Demo 9.0 KBGSAP 28 KBFreeDownload the zip

The menu hidden under the page: pressing Menu the whole page lifts off like a sheet of paper, tips in 3D and slides aside on two springs, its corners rounding, the light changing on its face and a soft shadow falling on the menu. Underneath, the links come in like steps; pressing the page or a link, the sheet comes back down and settles with a little tap.

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 menu under the page</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/hanken-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="scene" data-scene>
    <!-- the menu lies under the page: the page lifts off it, tips back and slides aside like a sheet of paper -->
    <nav class="menu" id="menu" aria-label="Main" data-menu inert>
      <ol class="menu__links">
        <li><a class="link" href="#" aria-current="page" data-link><span class="link__word" data-word>Index</span><svg class="link__arrow" viewBox="0 0 40 24" aria-hidden="true"><path d="M2 12h34M26 3l10 9-10 9"/></svg></a></li>
        <li><a class="link" href="#" data-link><span class="link__word" data-word>Projects</span><svg class="link__arrow" viewBox="0 0 40 24" aria-hidden="true"><path d="M2 12h34M26 3l10 9-10 9"/></svg></a></li>
        <li><a class="link" href="#" data-link><span class="link__word" data-word>Studio</span><svg class="link__arrow" viewBox="0 0 40 24" aria-hidden="true"><path d="M2 12h34M26 3l10 9-10 9"/></svg></a></li>
        <li><a class="link" href="#" data-link><span class="link__word" data-word>Journal</span><svg class="link__arrow" viewBox="0 0 40 24" aria-hidden="true"><path d="M2 12h34M26 3l10 9-10 9"/></svg></a></li>
        <li><a class="link" href="#" data-link><span class="link__word" data-word>Contact</span><svg class="link__arrow" viewBox="0 0 40 24" aria-hidden="true"><path d="M2 12h34M26 3l10 9-10 9"/></svg></a></li>
      </ol>
    </nav>

    <!-- the dark under the page, and the shadow the lifted page casts on the menu -->
    <div class="dark" data-dark aria-hidden="true"></div>
    <canvas class="shadow" data-shadow aria-hidden="true"></canvas>

    <!-- the page: a plain sheet of paper with the Menu button in the middle. Once lifted, the whole of it is a button
         that lays it back down -->
    <div class="sheet" data-sheet>
      <div class="page" data-page>
        <button class="burger" type="button" aria-expanded="false" aria-controls="menu" data-burger data-autoclic>
          <span class="burger__word">Menu</span>
          <span class="burger__icon" aria-hidden="true"><i></i><i></i></span>
        </button>
      </div>

      <!-- the light on the tilted page, and the button that covers it while it is lifted -->
      <div class="sheet__light" data-light aria-hidden="true"></div>
      <button class="sheet__cover" type="button" aria-label="Back to the page" data-cover data-autoclic></button>
    </div>
  </div>
  <p class="sr" aria-live="polite" data-live></p>

  <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.

23 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 menu under the page” from Animorio and fit it into my site

What it does

A cream sheet covers the whole screen, with only a dark Menu button in the middle, two short lines in a light circle beside the word. With the mouse on Menu the sheet lifts slightly from a corner and the dark underneath shows. Pressing it, the whole sheet comes away: it moves back, tips backwards, turns with its left edge towards the viewer and slides to the right taking the button with it, its corners rounding, a veil of light on its face and a soft shadow falling on the menu. The slide overshoots its spot slightly and comes back. Underneath, the menu comes out of the dark: five big links in a serif typeface, Index, Projects, Studio, Journal and Contact, with the current page written in gold, come in from the right one after the other like steps. Under the pointer a link grows fuller and softer on the variable typeface's axes, with a golden arrow, and the others dim; over the lifted sheet, the sheet sinks a little as if it wanted to come back. Pressing the sheet, a link or Esc, the sheet slides back, straightens and settles with a little tap; a link also becomes the current page and turns gold.

How it works

Two springs for a sheet

The page moves on two numbers, how far it is lifted and how far it has slid, each with its own damped spring (stiffness and damping in LIFT and SLIDE) worked out every frame in steps of 1/240 of a second, with the frame's step capped at 1/30. Opening, the lift starts first and the slide 0.07 seconds later; closing, the other way round. Coming down the page cannot go into the menu: it touches zero, bounces back a little and stops, and that is the little tap. Once both springs have settled (to a thousandth of the way) they are put exactly in place, drawn one last time, and from then on the loop touches nothing: not the transform, not the canvas, not the styles.

The matrix by hand

From the two numbers the script builds the page's matrix3d by multiplying matrices: a move aside and back, then round its middle the twist, the turn on the vertical axis (the left edge comes forward) and the one on the horizontal axis. So it knows where the four corners are in 3D. How far the page must slide is worked out on those projected corners, so that its left edge clears the menu's widest link at any screen width.

The shadow and the light

The corners are projected from a lamp up on the right onto the plane of the menu, which lies under the page: the shadow is that quadrilateral, drawn on a canvas only through its shadowBlur (the shape itself is moved off the canvas), in two layers: tight and dark near the sheet, wide and soft further off. The canvas is at a quarter of the resolution, which is plenty for a blurred patch. The light on the page comes from the angle between its face and the lamp, compared with the flat page: it is a veil of flat colour over the page, light if it faces the lamp more, dark if less.

The links like steps

The links come in from the right with expo.out, one every STEP seconds, starting a little turned round their bottom corner: they seem to come out from under the page. Under the pointer the Fraunces typeface changes its variable axes (weight, softness and the letters' «wonk») with a CSS transition on font-variation-settings, and the other links dim. The current page has its name in gold, with aria-current="page".

Real buttons and the keyboard

Menu is a button with aria-expanded; open, the page becomes inert and over it lies another button as big as the page that lays it back down. Esc closes, the Tab key goes round the links and the page, and the focus goes back to Menu. Before opening, the mouse or the focus on Menu lifts a corner of the page; a finger does it as soon as it touches.

Values to change

NameFileDefaultWhat it changes
LIFTscript.js[170, 0.7]The spring that lifts and tips the page: how stiff, and how much it is held back (1 = no bounce).
SLIDEscript.js[110, 0.58]The spring that slides the page aside: how stiff, and how much it is held back.
TILTscript.js9The degrees the page tips back, round its middle.
TURNscript.js17The degrees the page turns, its left edge coming towards you.
TWISTscript.js2The degrees the page twists as it lies aside.
BACKscript.js240How far the page moves away from you, in pixels.
STEPscript.js0.07The seconds between one link of the menu and the next as they come in.
DEPTHscript.js1500The distance of the eye, in pixels: less, and the 3D is stronger.

Try them in the Playground

When to use it

For the full-screen menu of a studio's site, a portfolio, an agency or a magazine: wherever opening the menu can be a small moment and the page is well-kept enough to be looked at tilted.

On phones

At 390 pixels the menu sits in a narrow column on the left and the arrows beside the links go; the slide is worked out on the real width, so the sheet leaves the links clear and an edge of it stays in sight to tap and close. Without a mouse, a finger on Menu lifts the corner as soon as it touches. The page does not scroll: the menu and the sheet fit the window.

With reduced motion

No springs and no sliding: the page fades out and fades back in already lifted aside, the menu and its links fade in, and closing the page fades out and back in its place. The links still change weight under the pointer.

Pitfalls

  • Tipped round its top edge with the perspective, the page came out towards the eye and grew huge: the tilt must go round the middle, and the page must be pushed back, not forward, for it to look lifted and whole.
  • The slide cannot be fixed: with the turn and the perspective the left edge ends up somewhere else at every width. It is worked out on the open matrix, three times in a row to settle it, measuring the links and not their rows (which are as wide as the screen) and taking off the shift they still have from the closing animation.
  • A shadow with filter: blur on a screen-sized element, redrawn every frame, was too slow: it is now a canvas at a quarter of the resolution with shadowBlur alone, and a blurred shadow loses nothing.
  • The shadow must be cast onto the plane of the menu, which lies lower than the lifted page: cast onto the page's own plane it matched the page and could not be seen.
  • With the menu open, whatever is on the page (here the Menu button, in a real site its links) would stay clickable under the transform: the page becomes inert and a single button covers it and lays it back down, with its own visible focus.
  • Shot at double density in a browser with no GPU, the tilted page took more than 30 seconds: three things weighed it down. The class that tells the compositor the page is moving stayed on even at rest, because the springs stopped a hair short of the threshold; the light was a gradient whose angle changed every frame; the menu's background was a window-sized gradient. Now the springs settle at a thousandth and the class goes off, the light is a flat colour and so is the background: the shot takes a third of the time.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Hanken Grotesk (font variabile) 5.3.0.

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

Testing

Tested on 4 October 2026

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

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 components