Skip to content
animorio
IT
All components

The elastic calendar

Calendars, Physics, SVG, TextGSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

A minimal calendar for the dates of a stay: once the arrival is chosen, a thin vermilion line leaves its day and follows the pointer like a rubber band, bending as it runs and straining against a night already taken; choosing the departure pins it down and it trembles like a plucked string, the nights in between darken one after the other and on the left the number of nights widens with the stay.

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 elastic calendar</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">
    <!-- the picker: a month to choose the dates of a stay. Once the arrival is chosen a thin elastic line leaves its
         day and follows the pointer between the days; choosing the departure pins it down and it settles like a plucked
         string, the nights in between darken one after the other and the big number stretches with the stay -->
    <div class="cal" data-picker data-autoclic>
      <section class="cal__stay" aria-label="Your stay">
        <p class="cal__label">Your stay</p>
        <div class="cal__count" aria-hidden="true">
          <span class="cal__num" data-num><span class="cal__digits" data-digits>0</span></span>
          <span class="cal__unit" data-unit>nights</span>
        </div>
        <dl class="cal__dates">
          <div class="cal__date">
            <dt>Arrive</dt>
            <dd data-from>Add date</dd>
          </div>
          <div class="cal__date">
            <dt>Leave</dt>
            <dd data-to>Add date</dd>
          </div>
        </dl>
        <button class="cal__clear" type="button" data-clear disabled><span>Clear dates</span></button>
      </section>

      <section class="cal__month" aria-label="Choose your dates">
        <div class="cal__head">
          <button class="cal__nav" type="button" aria-label="Previous month" data-prev><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14.5 6 8.5 12l6 6"/></svg></button>
          <h2 class="cal__title" aria-live="polite"><span data-title></span></h2>
          <button class="cal__nav" type="button" aria-label="Next month" data-next><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 6l6 6-6 6"/></svg></button>
        </div>
        <div class="cal__week" aria-hidden="true" data-week></div>
        <div class="cal__field" data-field>
          <svg class="cal__line" aria-hidden="true" data-svg>
            <circle class="cal__pin" r="19" data-pin="a"/>
            <circle class="cal__pin" r="19" data-pin="b"/>
            <path class="cal__band" d="" data-band/>
          </svg>
          <div class="cal__grid" data-grid></div>
        </div>
      </section>

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

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

What it does

On a white background, on the left the stay (Your stay, a big number of nights, the arrival, the departure and Clear dates) and on the right a month with its arrows. The days are grey numbers; the gone days and the days of the months either side are only hinted, the nights already booked have a thin line across them and can’t be chosen. The first click chooses the arrival: a vermilion ring draws itself around the number. From there a thin line follows the pointer between the days on a spring, drawn a little to the middle of the day under the pointer: if the pointer runs the line bends, the days of the stay turn dark grey and the number on the left counts the nights and widens or narrows with the line. A booked night holds it back: the line stops on the last free day and pulls towards the pointer. The second click chooses the departure: the line lands on its ring, which draws itself from where the line touches it, and trembles like a plucked string while the nights in between darken one after the other, each with a one-pixel nudge; the number gains weight. The numbers of the days sit on a little patch of white, so the line passes behind them and never crosses a figure. A new arrival makes the line snap back like a rubber band let go; Clear dates closes it into the arrival and turns the nights off backwards. The arrows change month: the days leave and come back in a diagonal wave, and the line with them. When the page opens a four-night stay is pinned in front of you.

How it works

The month in days and weeks

The script draws six weeks of cells, so every month is equally tall, and puts each date on a continuous grid: x in days from the left, y in weeks from the top. Dates outside the month have their place too (above or below), so a stay that begins in the month before comes in from the edge. The booked nights (about one in nine) come from a number worked out from the date, so they are always the same.

Two springs and two modes

The line has two ends, each on a spring towards its target: the arrival, and the pointer drawn to the middle of the day under it (FOLLOW, MAGNET) or the departure. The curve is the sum of two ways of vibrating, a half wave and a whole wave across the line: the acceleration of the two ends drives them (LAG), so the middle of the line lags behind when it runs, and the departure gives it a kick (PLUCK, RING). Four small steps per frame, with the step capped at 1/30 of a second, in one requestAnimationFrame that stops when nothing moves.

The line goes behind

The line is an SVG path of 29 points written again on every frame, from ring edge to ring edge rather than centre to centre; the longer it is the thinner it gets, because it is stretched. The numbers of the days sit on top, on a little patch of white background: the line vanishes behind every figure and reads as a row of strokes between one day and the next.

The rings and the nights

Each ring is a circle with stroke-dasharray: GSAP draws it in 0.45 seconds starting from the angle the line comes in at. Each day has a --k variable from 0 to 1 that mixes the grey and the black (color-mix) and takes the weight from 380 to 680 and the width from 96 to 104: once the departure is chosen GSAP takes it to 1 one night after the other (DARKEN), with a one-pixel nudge down and an elastic return.

The number that widens

The number of nights is set in Archivo, whose width axis goes from 62 to 125. On every frame the script counts the nights from where the free end is (a week down is worth seven nights), halfway too, and turns them into width (STRETCH): the number widens and narrows with the line, trembles with its swing and gains weight when the departure is chosen.

Keyboard, mouse and finger

A single day of the grid 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. With the keyboard the line follows the focus, with the mouse the pointer; with a finger, pressing a day stretches the line to it at once, and lifting the finger pins it.

Values to change

NameFileDefaultWhat it changes
WEEK_STARTscript.js1The first day of the week: 1 Monday, 0 Sunday.
MAX_NIGHTSscript.js14The longest stay, in nights: beyond it the line strains and goes no further.
FOLLOWscript.js3.6How quickly the free end of the line follows the pointer, in hertz.
MAGNETscript.js0.42How much the free end is drawn to the middle of the day under the pointer (0 to 1).
LAGscript.js1.5How much the line bends when the pointer runs (0 = it stays straight).
PLUCKscript.js11How far the line swings when the departure pins it, in pixels.
RINGscript.js5.2The pitch of the swing, in hertz: higher, a tighter string.
DARKENscript.js[0.34, 0.05]The nights darkening: the seconds of each, and the gap between one night and the next.
STRETCHscript.js[66, 124]The width of the number of nights, from one night to a long stay (between 62 and 125).
SLIDEscript.js0.55The seconds of a change of month.

Try them in the Playground

When to use it

For the booking page of a hotel, a holiday house, a B&B or a rental: wherever an arrival and a departure are chosen and the nights matter. The booked nights should come from your own system instead of the made-up ones.

On phones

Below 720 pixels the stay goes on top, with the number beside the dates, and the cells narrow to fit the width of the screen. A finger has no pointer: pressing a day stretches the line to it at once, with the same spring and the same bend, and lifting it pins it with the same swing.

With reduced motion

No springs, no swings and no waves: the line goes straight from one ring to the other at once, the rings and the nights fade in, the number takes its width at once, the months change with a fade and when the page opens the stay is already in place.

Pitfalls

  • A line from centre to centre crossed the figures of the days in between, like a strike-through (which here means «booked»): now it starts from the edge of the rings and every number sits on a little patch of white, so the line passes behind it.
  • Counting the nights from the day under the pointer made the width of the number jump at once: now they are counted from where the free end is, halfway too, and a week down is worth seven nights, so the width flows with the line.
  • Changing month, the rings were left floating over the empty grid while the days had gone: now the line leaves and comes back with the days, already in its new place.
  • With two figures the wider number came out of its column: ten nights or more are set a little smaller, on the same line, so nothing below moves.
  • The preview’s tour lives in a GSAP timeline, with the fake pointer being an object moved by tweens: so the preview video records the whole round, and a real hand stops it at once.

Libraries

GSAP 3.15.0, Archivo (font variabile, assi wght e wdth) 5.3.0.

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