Skip to content
animorio
IT
All components

The booking steps

Steppers, Transitions, SVGGSAPAdvancedCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A booking in four steps joined by a line that fills: the step done fills with colour from its middle as its icon spins away and a tick draws itself in its place, the panel slides out to one side and the next comes in while the card takes the new height; at the end a ring and a tick draw themselves and the steps make a wave.

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 booking steps</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/red-hat-display@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the booking steps: four steps joined by a line that fills as they are done; each step done turns its icon
         into a tick, and the panels under them slide one into the other while the card takes their height -->
    <div class="book" data-book data-autoclic>
      <div class="steps">
        <span class="steps__track" aria-hidden="true"><span class="steps__fill" data-fill></span></span>
        <ol class="steps__list" data-steps>
          <li class="step">
            <button class="step__node" type="button" aria-label="Dates">
              <span class="step__disc"></span>
              <svg class="step__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 7.5c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2zM5 10h14M9 3.5v4M15 3.5v4"/></svg>
              <svg class="step__tick" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 12.5l3.8 3.8 7.2-8"/></svg>
            </button>
            <span class="step__label">Dates</span>
          </li>
          <li class="step">
            <button class="step__node" type="button" aria-label="Room">
              <span class="step__disc"></span>
              <svg class="step__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 18.5V7M3.5 13h17v5.5M20.5 13v-1.5a3 3 0 0 0-3-3h-6v4.5M6 12.2a2 2 0 1 0 0-.01"/></svg>
              <svg class="step__tick" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 12.5l3.8 3.8 7.2-8"/></svg>
            </button>
            <span class="step__label">Room</span>
          </li>
          <li class="step">
            <button class="step__node" type="button" aria-label="Details">
              <span class="step__disc"></span>
              <svg class="step__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 12a3.6 3.6 0 1 0 0-7.2 3.6 3.6 0 0 0 0 7.2zM5 19.5c.8-3.2 3.6-5 7-5s6.2 1.8 7 5"/></svg>
              <svg class="step__tick" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 12.5l3.8 3.8 7.2-8"/></svg>
            </button>
            <span class="step__label">Details</span>
          </li>
          <li class="step">
            <button class="step__node" type="button" aria-label="Confirm">
              <span class="step__disc"></span>
              <svg class="step__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3.5h7.5L19 8v11a1.5 1.5 0 0 1-1.5 1.5h-10A1.5 1.5 0 0 1 6 19V5a1.5 1.5 0 0 1 1-1.5zM14 3.5V8h5M9.5 13h5M9.5 16.5h3"/></svg>
              <svg class="step__tick" viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 12.5l3.8 3.8 7.2-8"/></svg>
            </button>
            <span class="step__label">Confirm</span>
          </li>
        </ol>
      </div>

      <form class="book__form" novalidate data-form>
        <div class="book__panels" data-panels>
          <section class="panel" aria-labelledby="h-dates" data-panel>
            <h2 class="panel__title" id="h-dates" tabindex="-1">When are you coming?</h2>
            <fieldset class="group">
              <legend class="group__label">Arrival in June</legend>
              <div class="chips" data-chips="day"></div>
            </fieldset>
            <fieldset class="group">
              <legend class="group__label">How long</legend>
              <div class="chips" data-chips="stay"></div>
            </fieldset>
          </section>

          <section class="panel" aria-labelledby="h-room" hidden data-panel>
            <h2 class="panel__title" id="h-room" tabindex="-1">Choose a room</h2>
            <div class="rooms" role="radiogroup" aria-labelledby="h-room" data-rooms></div>
          </section>

          <section class="panel" aria-labelledby="h-details" hidden data-panel>
            <h2 class="panel__title" id="h-details" tabindex="-1">Who is coming?</h2>
            <label class="field">
              <span class="field__label">Your name</span>
              <input class="field__input" name="name" autocomplete="name" data-name>
              <span class="field__error" aria-live="polite" data-error></span>
            </label>
            <label class="field">
              <span class="field__label">Email</span>
              <input class="field__input" name="email" type="email" autocomplete="email" inputmode="email" data-email>
              <span class="field__error" aria-live="polite" data-error></span>
            </label>
          </section>

          <section class="panel" aria-labelledby="h-confirm" hidden data-panel>
            <h2 class="panel__title" id="h-confirm" tabindex="-1">All looks right?</h2>
            <dl class="summary" data-summary></dl>
            <div class="total"><span>Total</span><strong data-total>€0</strong></div>
          </section>

          <section class="panel panel--done" aria-labelledby="h-done" hidden data-panel>
            <svg class="done__mark" viewBox="0 0 80 80" aria-hidden="true"><circle cx="40" cy="40" r="34" data-ring/><path d="M26 41.5l9.5 9.5L55 31" data-check/></svg>
            <h2 class="panel__title" id="h-done" tabindex="-1">You’re booked</h2>
            <p class="done__text" data-done-text></p>
            <button class="btn btn--ghost" type="button" data-again>Make another booking</button>
          </section>
        </div>

        <div class="book__actions" data-actions>
          <button class="btn btn--ghost" type="button" data-back>Back</button>
          <button class="btn btn--main" type="submit" data-next>
            <span class="btn__clip"><span class="btn__label" data-next-label>Continue</span></span>
            <svg class="btn__arrow" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h13M13 6.5l5.5 5.5-5.5 5.5"/></svg>
            <svg class="btn__spin" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>
          </button>
        </div>
      </form>
    </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.

39 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 booking steps” from Animorio and fit it into my site

What it does

On a black background four round steps with their icons (a calendar, a bed, a person, a sheet) joined by a grey line, named Dates, Room, Details and Confirm; under them, a dark card with When are you coming?, the chips for the arrival day in June (Fri 12, Sat 13, Sun 14, Mon 15) and for the length (Two nights, Three nights, A week), the chosen ones honey-coloured, and a honey Continue button with an arrow. Choosing a chip, the honey fills it from the point pressed. Going on, the line fills up to the next step, the step done fills with honey from its middle while its icon spins away and a tick draws itself in its place, and the step reached gives a hop; the panel goes out to the left and the next comes in from the right with its lines rising one after another, while the card takes its height. In the room step three rooms with their prices: choosing one, its frame turns honey and its tick pops out. In the details, name and email: left empty or with half an email, the field turns red, shakes and says why underneath. In the last look, the summary with arrival, departure, room, guest and email, and a total counting up from zero; the button becomes Confirm booking, the words rolling and its width changing. Confirming, an arc turns in the button with Booking, then the last step ticks too, the panel becomes You’re booked with a ring and a tick drawing themselves, a line with the email and the day, and the four steps make a wave; Make another booking starts over. A step done can be clicked to go back to it, Back goes back by one.

How it works

The line and the steps

The line between the middle of the first step and the last is a bar scaled with scaleX to the step’s fraction in FILL seconds (power3.inOut). Every step has a honey disc, its icon and a tick: done, the disc grows from the middle with back.out, the icon turns and shrinks, the tick draws itself with stroke-dashoffset; the step reached hops with elastic.out when the line gets there (at 80 per cent of FILL). Going back, the steps undo from the right.

The panels crossing

The panel leaving is taken out of the flow (position absolute, where it was), so the card can measure its height with the new one; then in MOVE seconds the card goes from the old height to the new, the old panel slides away by SLIDE pixels as it fades, the new one comes in from the other side with its lines rising one after another. Meanwhile the card cuts whatever passes beyond its edge.

The card’s controls

The chips are radio buttons: the honey is a copy of the chip cut by a circle clip-path that grows from the point pressed (from the middle with the keys) to the farthest corner. The rooms are radio buttons too, with a tick that pops out. The details are checked on Continue: fields empty or half-written turn red, shake with keyframes and say why, then are checked again as they are typed; the card grows smoothly with the messages.

The summary and the total

On the last step the summary is written from the choices (the dates worked out on June 2026, the nights, the room’s price) and the total counts up from zero; the button’s words roll and its width goes from one word to the other. Confirming, an arc turns for a moment, then the last panel: the ring and the tick draw themselves and the steps make a wave (up and down, one after another).

Keys and the preview

It is a real form: Enter goes on, the steps done are buttons that go back (the others are disabled), the step in progress has aria-current step and every new panel takes the focus on its title. A click from a script (the library’s preview) changes a choice and goes on, types the details, books and starts over, without taking the focus.

Values to change

NameFileDefaultWhat it changes
SLIDEscript.js48How far, in pixels, a panel slides in and out.
MOVEscript.js0.5The seconds of the change between two panels.
FILLscript.js0.6The seconds the line takes to reach the next step.
ROOMSscript.js[['Garden room', 'Looks onto the lemon trees', 120], …]The rooms: name, a line about it and the price of a night.

Try them in the Playground

When to use it

For booking a room or a table, an order, a sign-up, a quote, a purchase in several stages: anywhere a long form is split into steps.

On phones

The card takes the width of the screen; the steps stay in a row with their icons, the chips wrap and the fields open the right keyboard (the email one for the email).

With reduced motion

The panels change at once, without sliding, the line and the steps reach their new state without animation, the honey in the chips is full at once, the total appears already counted and the ring and the tick at the end are already drawn.

Pitfalls

  • Animating the card’s height between two panels needs both measures: the panel leaving has to be taken out of the flow (position absolute where it was) before measuring the one coming in.
  • A clip-path as a string does not always animate: the browser may write it back in another form and the tween would jump. The circle’s radius is tweened as a number and the clip-path written on every frame.
  • The total counting up, if it were inside the panels’ change, would keep the button busy for the whole count: it counts on its own.
  • A hidden panel has no size: the chips chosen while their panel is not showing are filled with the circle in per cent, not in pixels.

Libraries

GSAP 3.15.0, Red Hat Display (font variabile) 5.3.0.

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