Skip to content
animorio
IT
Back to the library

The dog-ear transition

Transitions, TextGSAP, CSS onlyMediumCompressed weight: Demo 8.4 KBGSAP 28 KBFreeDownload the zip

A minimal page change: the page has its bottom corner turned down like a dog-ear in a notebook, and the next page already shows underneath. On a click the corner is pulled and the fold runs across the whole page, flat on the table, turning a little as it goes; the folded part shows the back of the paper with the print showing through in reverse. Where the crease has passed lies the new page, and its title widens out of the crease letter by letter.

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 dog-ear 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>
    <!-- three pages stacked like the leaves of a notebook: the one on top has its corner turned down -->
    <section class="page page--a" data-page="a" aria-label="Folded">
      <header class="bar">
        <span class="brand">Octavo</span>
        <nav class="nav" aria-label="Pages">
          <a href="#folded" aria-current="page">Folded</a>
          <a href="#sewn" data-go="b">Sewn</a>
          <a href="#bound" data-go="c">Bound</a>
        </nav>
      </header>
      <div class="hero">
        <h1 class="title" data-title>Folded.</h1>
        <p class="lede">One large sheet, folded three times: sixteen pages that already know their order.</p>
      </div>
      <footer class="foot">
        <span>A bindery in two rooms</span>
        <a class="turn" href="#sewn" data-go="b" data-autoclic>
          <span class="turn__text">Turn to Sewn</span>
          <svg class="turn__ear" viewBox="0 0 14 14" aria-hidden="true"><path d="M1.5 1.5h11v7l-4 4h-7z"/><path d="M12.5 8.5h-4v4"/></svg>
        </a>
      </footer>
    </section>

    <section class="page page--b" data-page="b" aria-label="Sewn">
      <header class="bar">
        <span class="brand">Octavo</span>
        <nav class="nav" aria-label="Pages">
          <a href="#folded" data-go="a">Folded</a>
          <a href="#sewn" aria-current="page">Sewn</a>
          <a href="#bound" data-go="c">Bound</a>
        </nav>
      </header>
      <div class="hero">
        <h2 class="title" data-title>Sewn.</h2>
        <p class="lede">Each section is sewn through its fold with linen thread, by hand, at one long bench.</p>
      </div>
      <footer class="foot">
        <span>A bindery in two rooms</span>
        <a class="turn" href="#bound" data-go="c" data-autoclic>
          <span class="turn__text">Turn to Bound</span>
          <svg class="turn__ear" viewBox="0 0 14 14" aria-hidden="true"><path d="M1.5 1.5h11v7l-4 4h-7z"/><path d="M12.5 8.5h-4v4"/></svg>
        </a>
      </footer>
    </section>

    <section class="page page--c" data-page="c" aria-label="Bound">
      <header class="bar">
        <span class="brand">Octavo</span>
        <nav class="nav" aria-label="Pages">
          <a href="#folded" data-go="a">Folded</a>
          <a href="#sewn" data-go="b">Sewn</a>
          <a href="#bound" aria-current="page">Bound</a>
        </nav>
      </header>
      <div class="hero">
        <h2 class="title" data-title>Bound.</h2>
        <p class="lede">Cloth boards, a flat spine, and paper soft enough to keep a corner turned down.</p>
      </div>
      <footer class="foot">
        <span>A bindery in two rooms</span>
        <a class="turn" href="#folded" data-go="a" data-autoclic>
          <span class="turn__text">Back to Folded</span>
          <svg class="turn__ear" viewBox="0 0 14 14" aria-hidden="true"><path d="M1.5 1.5h11v7l-4 4h-7z"/><path d="M12.5 8.5h-4v4"/></svg>
        </a>
      </footer>
    </section>

    <!-- the folded part of the top page: its back, with the print showing through, and the light on the crease -->
    <div class="fold" data-fold aria-hidden="true">
      <div class="flap" data-flap>
        <div class="flap__ghost" data-ghost></div>
        <div class="flap__shade" data-shade></div>
      </div>
    </div>

    <!-- the turned-down corner is also a button -->
    <button class="corner" type="button" data-corner aria-label="Turn the page"></button>
  </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.

41 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 dog-ear transition” from Animorio and fit it into my site

What it does

The site of a made-up little bindery, Octavo, in three pages and almost nothing else: Folded. on cream paper, Sewn. on black, Bound. on stone grey; one huge Archivo headline with a vermilion full stop, a line of text and at the bottom the link to the next page. On load the letters of Folded. arrive very narrow and thin and widen one after another as they take weight, the full stop drops in last, and then the bottom right corner turns down into a dog-ear with a small bounce: the back of the paper shows, a little darker, with its shadow, and the triangle it uncovers already shows the black of the next page. With the pointer near the corner the dog-ear follows it: the tip of the paper goes where the pointer is and the crease turns with it; moving away lets it fall back. A click on Turn to Sewn (or on the corner) takes the paper and lifts it for a moment, then pulls it: the crease runs diagonally over the whole page, turning by almost thirty degrees, the folded part lies down on the old page showing its back with the headline showing through in reverse, and behind the crease the new page is uncovered. The new headline widens right out of the crease: each letter, when the crease passes over it, starts narrow and thin and widens on a measured spring, pushing its neighbours; the vermilion full stop drops in at the end. Then the new page turns its own corner down, and under it is the page after. The three pages go round in a circle; the names at the top lead to the one chosen.

How it works

A fold is a reflection

A page folded flat is drawn with two clips and a mirror. You choose a direction u (from the corner into the page) and the crease's distance d from the corner, and for every point you measure g, how far it is from the corner along u. The top page keeps, with clip-path polygon, only the part where g is greater than d; the flap is a page-sized copy, clipped to the part where g is less than d and then mirrored over the crease with a single CSS matrix(). Clipping a rectangle with a half-plane is the classic Sutherland-Hodgman on four vertices.

The corner, the tip and the pointer

The whole state is an object with two numbers: s, how far the tip of the corner has moved, and phi, its direction; the crease sits at half of s. The dog-ear at rest has phi 45 and s equal to EAR times the square root of two, so both folded sides are EAR long. With the pointer near the corner, s and phi come from the pointer's position (the tip goes right there), with phi between 20 and 70 degrees, and a GSAP tween chases them. A single ticker redraws the fold when s or phi change.

The back of the paper

The flap has the colour of the page's back and inside it a copy of the page itself (cloneNode, without links or attributes) at SHOW_THROUGH opacity: the mirror of the fold flips it by itself, and the print shows through in reverse as on a real sheet. On top there is a rotated band with a gradient starting at the crease: a one-pixel dark thread (the crease), a flash of light and a faint shadow towards the tip. The flap's shadow is a drop-shadow on its container, fading out as the crease leaves the page.

The title widening out of the crease

The new title is split into inline-block letters, with font-stretch and font-weight tied to --w and --g (Archivo runs from 62 to 125 in width). Under the old page the letters wait at --w 62 and --g 220. When the change starts, the moment the crease uncovers each letter is worked out in advance, sampling the same power2.inOut curve as the pull, and the letter's tween with back.out(2) goes into the timeline right there: each letter widens as the crease passes over it. The title is measured with the letters at 106, above the top of the bounce.

Direction, phone and reduced motion

Each change is one timeline with the labels grab, pull and turned, and timeScale for SPEED: the grab, the pull, the pages swapping once the crease has left, then the new page turns its corner down 2.5 pixels past its place and back. A click that comes while the page is turning waits in a queue. On a phone it is the same fold on a tall screen, with a tap on the link or the corner. With reduced motion nothing folds: the old page fades away and the new words fade in.

Values to change

NameFileDefaultWhat it changes
EARscript.js46How big the dog-ear is: the pixels along each of the two sides of the turned-down corner.
PULLscript.js1.3The seconds the fold takes to run across the page, from the corner to the far side.
SWEEPscript.js28How many degrees the crease turns while the page is pulled: 0 = it stays on the diagonal, more = the page turns sideways as in a book.
PEELscript.js150How many pixels the corner follows the pointer when it comes near: 0 = it stays put.
SHOW_THROUGHscript.js0.07How much the print shows through the back of the paper: 0 = thick paper, 0.15 = tissue paper.
WIDENscript.js0.95The seconds each letter of the new title takes to widen out of the crease.
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 about paper and books: a bindery or a stationer, a small publisher, a magazine, a personal blog made like a notebook, an illustration or editorial design portfolio. The dog-ear in the corner is also an invitation to turn the page, so it suits a story told in chapters or a menu to leaf through.

On phones

The same fold with the sizes adapted: the tall screen makes the crease run more upright, the titles shrink to fit the width even at the widest point of their bounce and the link at the bottom moves over to leave room for the dog-ear. A tap on the link or the corner turns the page; the corner following the pointer is there only with a mouse.

With reduced motion

Nothing folds or moves: the dog-ear stays drawn still in the corner, the old page fades away onto the bare new page and then its words fade in, with the title already in place.

Pitfalls

  • A variable called top in a classic script throws: window.top already exists and cannot be redeclared with let. The page on top is called topKey.
  • The fold is flat, so no 3D and no perspective: a page folding on the table is a 2D reflection. With rotateY and preserve-3d the two halves would be seen edge-on and the diagonal crease would not work.
  • The mirrored flap falls past the edge of the screen once the crease is past halfway: the pull ends when the crease has passed the opposite corner (d greater than the page's projection on u), not when the tip leaves the screen, or a triangle of the old page is left behind.
  • clip-path clips clicks too: under the dog-ear the page below would get the tap. Pages that are not on top have pointer-events none and inert, and the corner is a triangular button above everything.
  • With the dog-ear following the pointer, a pointer on the link got it covered by the paper. Over the link the corner no longer follows the pointer and only lifts a little.
  • A tween per letter started when the crease uncovers it (checked on every frame) would not slow down with SPEED or the previews' clock: the moment is worked out beforehand, sampling the pull's curve, and the tween goes into the timeline.
  • With reduced motion a crossfade of two huge titles blends them into one word: the new page appears bare and the words come after.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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