Skip to content
animorio
IT
All components

The menu to leaf through

Books, 3D, TransitionsGSAPAdvancedCompressed weight: Demo 7.7 KBGSAP 28 KBFreeDownload the zip

An osteria's menu as a little book: the page is turned by taking its edge, which follows the finger round the spine in 3D; it darkens as it rises and throws its shadow on the page below, and let go past the middle it turns over by itself, otherwise it falls back. On a computer it lies open on two pages, on a phone it turns one page at a time.

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 to leaf through</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/eb-garamond@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the menu to leaf through: a page turned by its edge follows the finger round the spine, darkens as it rises
         and throws its shadow on the page below; let go past the middle it turns over, otherwise it falls back -->
    <section class="menu" aria-label="The osteria's menu" data-menu data-autoclic>
      <div class="book" tabindex="0" aria-roledescription="book" data-book>
        <div class="book__shade book__shade--left" aria-hidden="true" data-shade-left></div>
        <div class="book__shade book__shade--right" aria-hidden="true" data-shade-right></div>
      </div>
      <nav class="leaf" aria-label="Pages">
        <button class="leaf__btn" type="button" aria-label="Previous page" data-prev><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6l-6 6 6 6"/></svg></button>
        <p class="leaf__where"><span class="roll" data-where></span></p>
        <button class="leaf__btn" type="button" aria-label="Next page" data-next><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 6l6 6-6 6"/></svg></button>
      </nav>
      <p class="sr" aria-live="polite" data-live></p>
    </section>
  </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.

83 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 to leaf through” from Animorio and fit it into my site

What it does

On a white background the menu of the Osteria La Lanterna, closed: a green cover with a lantern drawn in gold, which as soon as it arrives opens a crack and falls shut; below, the arrows and the name of the pages. Taking the edge of the right page and dragging it to the left the page rises round the spine and its edge stays under the finger; as it rises it darkens, its shadow appears on the page below, and past the middle its back with the next page shows. Let go past the middle, or with a quick flick, the page finishes turning; before that, it falls back into place with a little bounce. The open book slides over with the spine in the middle: Antipasti and Primi, Secondi and Dolci, Vini and a Grazie with the kitchen's hours, and the name under the book rolls at every page. With the mouse near the edge the corner lifts a little, as if inviting to turn it. On a phone one page shows at a time, and the turned page fades into the spine.

How it works

Sheets with two faces

Each sheet hangs from the spine (transform-origin on its left edge) and has two faces: front and back, turned 180 degrees, both with backface-visibility hidden; the sheet has transform-style preserve-3d and the perspective is on the book. Open on two pages a sheet carries the right page on its front and the left one on its back; one page at a time, it carries a single page and a blank back.

The edge under the finger

The sheet's angle is the one whose projection on the table reaches the pointer: −acos(distance from the spine / page width). So the edge stays under the finger; a 0.18-second tween takes it there, so that a sheet taken from the spine opens without jumps.

Light and shadow

On each face there is a dark veil whose opacity is the sine of the angle: nothing with the page lying flat, full when edge-on. On the page below a shadow falls from the spine, on the right while the sheet rises and on the left when it comes down on the other side.

Letting go, tapping, turning

Let go past the middle, or with a flick faster than FLICK pixels a second, the sheet goes to −180 or to 0; otherwise it comes back with back.out. A tap on the right turns forwards, on the left backwards. On a phone the finger cannot go past the spine, so two thirds of the way is enough, and past the spine the sheet fades. Turned pages lie on the left with a growing z-index, pages to turn on the right with a falling one, and the moving sheet above all.

The closed book, the keys and the preview

Closed, the book moves by half a page so the cover sits in the middle, and opening it comes back with the spine in the middle. The arrows under the book and those of the keyboard turn the pages, Home and End go to the start and the end; with the mouse near the edge the corner lifts by LIFT degrees. A click from a script (the library's preview) turns the next page and, at the end, closes the book page after page.

Values to change

NameFileDefaultWhat it changes
PLACEscript.js['Osteria', 'La Lanterna']The name on the cover, in two lines.
PAGESscript.js[['cover'], ['Starters', 'Antipasti', [['Bruschetta', 'Tomatoes, basil, new oil', 7], …]], …, ['thanks']]The pages: the name under the book, the title on the page and the dishes (name, what is in it, price); cover and thanks are the cover and the last page.
TURNscript.js0.75The seconds a page takes to turn by itself.
LIFTscript.js8The degrees the corner lifts under the mouse.
FLICKscript.js700The speed, in pixels a second, that turns a page even before the middle.

Try them in the Playground

When to use it

For the menu of a restaurant or a farm stay, a wine list, a catalogue, a brochure, a wedding booklet, a magazine: anything that reads better leafed through.

On phones

Under 640 pixels the book shows one page at a time, up to 360 pixels wide: the finger turns it towards the spine, from right to left, and brings it back starting from its left third; the page does not scroll the screen (touch-action none on the book).

With reduced motion

Pages turn at once, with the buttons, the keys and letting go of the finger (which still moves the page while dragging); the corner does not lift with the mouse and no entrance.

Pitfalls

  • The two faces need backface-visibility hidden (in Safari with the -webkit- prefix too) and the sheet transform-style preserve-3d; the perspective goes on the parent, not on the sheet.
  • An angle proportional to the movement makes the page run away from the finger: the right angle comes from the arccosine of the distance from the spine.
  • On a phone the finger cannot go past the spine and 90 degrees is never reached: there the threshold for turning must be lower.
  • One page at a time there is no left page: the backs would never be seen, so there each page sits on the front of a sheet of its own.
  • The order of the sheets changes while leafing: z-index growing on the left, falling on the right, the moving sheet above all, and the pile put back in order when it lands.

Libraries

GSAP 3.15.0, EB Garamond (font variabile) 5.3.0.

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

Testing

Tested on 3 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