Skip to content
animorio
IT
All components

The paper calendar

Calendars, 3D, SVGGSAPAdvancedCompressed weight: Demo 7.5 KBGSAP 28 KBFreeDownload the zip

A wall calendar on a black background, a cream sheet for each month hanging from a wire binding: turning the page the sheet lifts up over the binding in 3D, darkening and showing its back, and the chosen day is circled by hand with a red marker.

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 paper calendar</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz-italic.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <div class="calendar" data-calendar>
      <!-- the arrows (below the pad on screen, first for the Tab key) -->
      <div class="calendar__nav">
        <button class="calendar__arrow" type="button" aria-label="Previous month" data-prev><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 15l6-6 6 6"/></svg></button>
        <button class="calendar__arrow" type="button" aria-label="Next month" data-next data-autoclic><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
      </div>

      <!-- the pad: a wire binding, the sheet of the month, and the edges of the sheets below.
           Turning a page, a copy of the sheet swings up over the binding in 3D while the new month waits underneath -->
      <div class="pad" data-pad>
        <div class="pad__sheets" data-sheets></div>
        <svg class="pad__wire" data-wire aria-hidden="true"></svg>
      </div>
      <p class="sr" role="status" aria-live="polite" data-said></p>
    </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.

13 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 paper calendar” from Animorio and fit it into my site

What it does

On a black background a wall calendar: a cream sheet hanging from a metal wire binding, with the edges of the sheets below peeking out at the bottom. On the sheet are the year, the name of the month in italics, the initials of the days and the numbers, with Sundays in red; below, two arrows. The arrow down turns the page: the sheet lifts up turning around its top edge, goes over the binding darkening as it turns away from the light, shows its back and fades, while the next month waits underneath, in the shadow of the lifting sheet. The arrow up brings last month’s sheet back down over this one. A swipe on the calendar turns the page too: upwards forward, downwards back. Under the pointer a number lifts and tilts a little. Pressing a day, a red marker circles it by hand: a slightly crooked, shaky ring that runs a little past where it began; choosing another day the old ring rubs out running backwards. When the page opens the calendar comes down from above and the marker circles a day.

How it works

The sheet

Each month is a section drawn by the script: the year, the month, the initials of the days from WEEK_START on and six weeks of buttons. Over the days sit an empty SVG for the ink and a dark veil for the shadow. The binding is an SVG drawn again for the width of the calendar: a loop about every 24 pixels, each with its hole in the paper and two wires.

The turning page

The moving sheet goes into a box with transform-style: preserve-3d and its pivot on the top edge, together with a back face turned by 180 degrees; both have backface-visibility: hidden. A number goes from 0 to 180 degrees in FLIP seconds with power2.inOut, and on every frame it becomes the box’s rotationX. The container has the perspective (DEPTH pixels) seen from the top edge, so the sheet lifts towards you.

Light and shadow

The same number darkens the turning sheet, with a veil that reaches LIGHT at 90 degrees, and the sheet below, with LIGHT times 0.8 times the sine of the angle: the shadow is there while the sheet is lifted. Past 120 degrees the two faces fade, each on its own.

The marker

The ring is an ellipse of 72 points, slightly tilted, with a shake made of a sine and a little chance (WOBBLE), that goes round 1.14 times and widens a little as it goes. The chance comes from a generator seeded with the date, so the same day always gets the same ring. It is drawn by taking the stroke-dashoffset from the length of the stroke to zero in INK seconds, and rubbed out the other way.

Keyboard and finger

The arrows come before the calendar in the code (and below it on screen, with order), so the Tab key finds them first; then a single day takes the Tab key: the arrow keys move between the days, Page Up and Page Down turn the pages, Enter circles. With a finger a swipe of at least 40 pixels turns the page, and the tap it ends with chooses no day.

Values to change

NameFileDefaultWhat it changes
WEEK_STARTscript.js1The first day of the week: 1 Monday, 0 Sunday.
FLIPscript.js1The seconds of a page turning.
LIGHTscript.js0.35How dark a sheet gets as it turns away from the light, and the shadow it throws on the one below.
INKscript.js0.6The seconds the marker takes to draw a ring.
WOBBLEscript.js0.07How shaky the hand is: 0 draws a perfect ellipse.
DEPTHscript.js1600The perspective, in pixels: the smaller it is, the more the sheet comes towards you.

Try them in the Playground

When to use it

For the events page of a venue, a theatre or a festival, for an advent calendar or a countdown: wherever one month at a time is shown and the calendar can also be an object.

On phones

The calendar narrows to the width of the screen and taps choose the days like clicks. On top of that, a swipe upwards turns the page and a swipe downwards goes back.

With reduced motion

The page doesn’t turn: the new month fades in over the old one in a quarter of a second. The marker doesn’t draw, the ring appears already made, and when the page opens the calendar is already in place.

Pitfalls

  • Opacity on the 3D box flattens it: the front would show through the back, mirrored and dark. That is why the final fade goes on the two faces, one by one, and never on the box.
  • The turning sheet is the very sheet that was there, moved into the box: no copies to keep aligned. Going back, it is last month’s sheet that goes into the box already turned by 180 degrees and comes down.
  • The ring is drawn on the day’s measures inside the sheet (offsetLeft, offsetTop), not on its rectangle on the screen, which the perspective distorts during the turn; when the window changes it is drawn again.
  • A page turned while another is still turning sends that one straight to its end (progress(1)): two pages in the air at once would get stuck in each other.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 7.5 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