Skip to content
animorio
IT
All components

The zooming calendar

Calendars, TransitionsGSAPMediumCompressed weight: Demo 8.1 KBGSAP 28 KBFreeDownload the zip

The whole year in twelve small months: choosing one, the view goes into it, the small month growing into the big one while the rest of the year swells past the edges and fades, like a camera moving 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 zooming calendar</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the calendar: the whole year in twelve small months; choosing one, the view zooms into it until it is the big
         month, and the way back zooms out to the year. The months are drawn by the script -->
    <div class="zoomcal" data-cal>
      <div class="zoomcal__head">
        <button class="zoomcal__back" type="button" data-back hidden><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6 8.5 12l6 6"/></svg><span data-back-label></span></button>
        <h1 class="zoomcal__title" data-title></h1>
        <div class="zoomcal__arrows">
          <button class="zoomcal__arrow" type="button" data-prev><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6 8.5 12l6 6"/></svg></button>
          <button class="zoomcal__arrow" type="button" data-next><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 6l6 6-6 6"/></svg></button>
        </div>
      </div>
      <div class="zoomcal__views" data-views>
        <div class="year" data-year></div>
        <div class="month" data-month hidden></div>
      </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 zooming calendar” from Animorio and fit it into my site

What it does

On a white background the whole year in twelve small months, four to a row (three on a phone), with the year at the top in the middle and two arrows on the right. The month of today has an orange name and today is an orange disc; the chosen day is a black disc; Saturdays and Sundays are grey. Pressing a month the view goes into it: the small month grows into the big one, its numbers become the big numbers without doubling, and the rest of the year swells past the edges and fades, like a camera moving in. At the top the name of the month takes the place of the year and on the left a button with the year appears, to go back: the big month shrinks into its place among the others while the year comes back around it. In the big month today has an orange number, and pressing a day chooses it with a black disc, which shows in the small month too. The arrows change year in the year view, with the months leaving on one side and coming in on the other one column after another, and change month in the month view, with the days coming in one column after another from the side of the arrow. On a phone a sideways swipe does what the arrows do. When the page opens the months arrive in a wave from the top left corner.

How it works

The months

The script draws the twelve small months and, when needed, the big one, always six weeks long: so the big month has the shape of its small one. The size of the numbers is a fraction of the cell (--num: 0.36 on a computer, 0.42 on a phone) taken from the month’s width with container units (cqi): small or big, the numbers fill the same part of their cell.

The camera

Before the zoom, the days of the small month and those of the big month are measured at rest. The ratio between the two widths says how much to enlarge: the year, with its origin at the top left, goes to that scale, moved so that the small month’s days land exactly on the big month’s days; the big month’s days start from the inverse scale, exactly over the small ones. The two moves have the same duration (ZOOM) and the same curve (EASE), so they stay one over the other on every frame.

The fades

Meanwhile the year fades over the first part of the zoom (FADE) with power2.in and the big month appears with power2.out; the names of the weekdays arrive last. Going back, the opposite happens. While the year moves, the container fades its edges over 16 pixels with a mask, instead of cutting them sharp.

Years and months

Changing year, a copy of the old year stays over the new one for as long as it takes to leave: its columns move out by 20 pixels and fade, the new ones come in from 28 pixels on the other side, STAGGER seconds one after another. Changing month, the old days leave sideways and the new ones come in column by column; the names of the days stay still.

The keyboard

A single small month takes the Tab key: the arrows move between the months, Page Up and Page Down change year, Enter opens. In the big month a single day takes the Tab key: the arrows move between the days, Home and End go to the start and the end of the week, Page Up and Page Down change month, Enter chooses and Esc goes back to the year. The focus follows the zoom only when the keyboard is in use: after a tap, Safari on iPhone would draw the focus ring on it.

Values to change

NameFileDefaultWhat it changes
WEEK_STARTscript.js1The first day of the week: 1 Monday, 0 Sunday.
ZOOMscript.js0.8The seconds of the zoom, in and out.
EASEscript.js'expo.inOut'The curve of the zoom.
FADEscript.js0.6The part of the zoom over which the year fades away (and comes back).
STAGGERscript.js0.05The seconds between one column of months and the next when the year changes.

Try them in the Playground

When to use it

For a planner, a diary, the events page of a season or a long-term booking: wherever you need to see the whole year and then go into a month, without losing track of where you are.

On phones

Below 720 pixels the year sits in three columns and the numbers fill a slightly larger part of their cell (--num 0.42), to stay readable. Taps work like clicks, and a sideways swipe changes year or month.

With reduced motion

No zoom: the year and the month swap with a fade, years and months change at once, the disc of the chosen day appears without a pop and nothing arrives when the page opens.

Pitfalls

  • The zoom must be measured at rest: the year without a transform and the big month in its place. Going back, the year’s scale is taken off for a moment, while the year is invisible, and put back after measuring.
  • With numbers of different sizes in the two months, halfway through the zoom you would see two overlapping numbers of different sizes: that is why the size is the same fraction of the cell in both.
  • The copy of the year that leaves is inert and hidden from screen readers, and loses the mark for the automatic preview: otherwise for half a second there would be two years to use.
  • The back button has display: inline-flex, which wins over the hidden attribute: it needs a [hidden] rule with display: none, or the button stays there all the time.

Libraries

GSAP 3.15.0, Geist (font variabile) 5.3.0.

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