
The three-line menu
Menus, Transitions, Physics, Text
NewFree
The four steps of a checkout, Bag, Delivery, Payment and Review, set large in a row that always fills the same measure to the pixel: the current step takes the room, wide and heavy, and the others squeeze on the width axis to make it. Going on, the room moves to the next name on a spring and the weight follows a moment later; the steps done stay black but light. Thin rules part the steps, and the two around the current one are in the accent colour. Minimal style.
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 steps that share the line</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage" data-stage>
<!-- the checkout steps: four names that share one measure to the pixel. The current step takes the room, wide
and heavy; the others squeeze on the width axis to make it. Going on, the room flows to the next name on a
spring and the weight follows a beat later; the steps done stay in ink, light. Thin rules mark where one
step ends and the next begins; the script places them, and the two around the current step are in the accent -->
<section class="steps" aria-label="Checkout" data-steps>
<div class="steps__row">
<ol class="steps__list" data-row>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__mask"><span class="step__name">Bag</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__mask"><span class="step__name">Delivery</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__mask"><span class="step__name">Payment</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__mask"><span class="step__name">Review</span></span></button></li>
</ol>
<div class="steps__rules" aria-hidden="true" data-rules></div>
</div>
<!-- what is in the current step: it travels with the step's left edge -->
<p class="steps__note" aria-live="polite" data-note><span class="note__track" data-note-track><span class="roll__item">2 items · $148.00</span></span></p>
<div class="steps__actions">
<button class="back" type="button" data-back>
<svg class="back__arrow" viewBox="0 0 16 14" aria-hidden="true"><path d="M15 7H1.5M7 1.5L1.5 7 7 12.5"/></svg>
<span class="back__text">Back</span>
</button>
<button class="next" type="button" data-next data-autoclic>
<span class="next__body" aria-hidden="true"></span>
<span class="next__text" data-next-text><span class="roll__item">Continue</span></span>
<svg class="next__arrow" viewBox="0 0 16 14" aria-hidden="true"><path d="M1 7h13.5M9 1.5L14.5 7 9 12.5"/></svg>
</button>
</div>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
36 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The steps that share the line” from Animorio and fit it into my siteOnce the typeface is in (document.fonts.load), every name is written 154 times in a hidden box, at 22 values of the wdth axis and 7 weights, at 100 pixels; the widths are read all together, in one layout of the page, and kept divided by the size of the type, because they grow with it. The opsz axis stays fixed at 96 both in the table and on the page: left free, it would change the widths with the size of the text.
Where the room is and where the weight is are two numbers in steps (0 is Bag, 4 the order placed), each on its own spring, integrated together in steps of 1/240 of a second with the frame step capped at 1/30. The room’s spring, ROOM_SPRING, is stiffer and goes a breath past; the weight’s, WEIGHT_SPRING, is softer and does not bounce: the new name first opens and then fills in. How current a step is is a bell one step wide around the number; how done it is, a smoothstep.
Every frame each name says how wide it would like to be, from the table, at its natural axis (WIDE for the current step, NARROW for the others) and its weight. The measure, less the room of the steps, is shared out in proportion; then for each name the table gives, with a linear interpolation, the wdth value that makes it exactly as wide as its share. A short name can run out of axis (Bag, at its widest, is still short): it keeps its widest and the others share what is left, until the sums come out.
The vertical rules stand where one step ends and the next begins, so they travel on the same spring with no animations of their own; their colour is mixed in CSS with color-mix between grey and vermilion by how much they frame the current step. When the room reaches the step it was going to, the two rules around it stretch by BLOW and come back on a short elastic; once the order is placed they all do, one after the other. The blow goes on the rule’s inner element, because the script moves the outer one.
The line under the steps follows the left edge of the current step, worked out from the same columns, and never leaves the measure; its words and the button’s roll, the old ones leave first and the new ones rise after. The pill takes the width of the new words. The steps done are buttons with their own aria-label, the current one has aria-current="step", Back stays reachable with aria-disabled; the right and left arrow keys go on and back. A step done under the pointer takes HOVER_WEIGHT of weight, and the solver narrows it on the wdth axis so that it stays in its column.
A click made by a script (isTrusted false, the preview’s) does not take a step but starts a GSAP timeline that shows every state: two steps on, one back, on again to the last, the order placed and the way back to the first step. A real click, a touch or a key stops it where it is; when nothing moves, the frame loop switches off.
| Name | File | Default | What it changes |
|---|---|---|---|
NOTES | script.js | ['2 items · $148.00', …] | The line under the steps: one for each step and one more for the order placed. |
ACTIONS | script.js | ['Continue', 'Place order', 'Keep shopping'] | The button’s words: on the way, on the last step and once the order is placed. |
ROOM_SPRING | script.js | [230, 0.74] | The spring that hands the room from step to step: how stiff it is and how damped (1 means no overshoot). |
WEIGHT_SPRING | script.js | [95, 0.9] | The spring of the weight, which comes after the room: how stiff and how damped. Softer, the new name stays open and thin longer before it fills in. |
WEIGHT | script.js | [300, 820, 230] | The weight of a name: a step to come, the current step, a step done (100 to 1000). |
WIDE | script.js | 128 | The width of the typeface (wdth axis) the current step aims for, before the measure is shared out (25 to 151). |
NARROW | script.js | 44 | The same for the other steps (25 to 151): lower, the more room the current step takes. |
HOVER_WEIGHT | script.js | 160 | How much weight a step done takes under the pointer: it stays in its column all the same. |
MEASURE | script.js | 620 | The widest the row may be, in pixels. |
MAX_SIZE | script.js | 48 | The biggest the names may be, in pixels: once there, it is the measure that comes in to meet the names. |
BLOW | script.js | 0.16 | How much the two rules around the step stretch when the room lands on it (0 to 0.6). |
For the checkout of an online shop, a booking, a sign-up or a long form split into three, four or five steps with short names: wherever the steps are also the page’s headline and the type alone has to say how far along you are, with no numbers, icons or circles.
The same animations with the sizes adapted: the row takes the width of the screen and the names get smaller just enough to fill it, still to the pixel; the line under the steps stays inside the measure even when the current step is the last, and the two buttons go to the two sides, Back on the left and the pill on the right. A touch goes on, back, and back to a step done by touching it.
No springs and no blow on the rules: the room, the weight and the rules are at once in their new place and the row of steps comes back with a short fade; the words under the steps and in the button change with a fade, the old ones first and the new ones after. On opening everything fades in.
GSAP 3.15.0, Roboto Flex (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 10 KB; the libraries: GSAP 28 KB.
Tested on 18 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.