
The cookie banner
Consent, Transitions, SVG, Physics
NewFree
The four steps of a checkout, Cart, Shipping, Payment and Review, over a thin line: going on, the head of the line runs to the next step in the accent colour and the ink catches it up a moment later on a spring; going back, the line winds itself in. Where the ink lands the line trembles a pixel between two ticks, like a string; the step done moves aside, takes weight and its tick draws itself. 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>Steps with the advancing line</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 checkout steps: one thin line under four names. Going on, the head of the line runs to the next tick and
the tail catches it up on a spring; going back, the line winds itself in. The steps done take weight and a
tick drawn by hand, the current one has its name in the accent -->
<section class="flow" aria-label="Checkout" data-flow>
<ol class="flow__steps" data-steps>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__lift"><svg class="step__check" viewBox="0 0 14 12" aria-hidden="true"><path d="M1.6 6.6l3.5 3.4 7.3-8.2"/></svg><span class="step__name">Cart</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__lift"><svg class="step__check" viewBox="0 0 14 12" aria-hidden="true"><path d="M1.6 6.6l3.5 3.4 7.3-8.2"/></svg><span class="step__name">Shipping</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__lift"><svg class="step__check" viewBox="0 0 14 12" aria-hidden="true"><path d="M1.6 6.6l3.5 3.4 7.3-8.2"/></svg><span class="step__name">Payment</span></span></button></li>
<li class="step" data-step><button class="step__btn" type="button"><span class="step__lift"><svg class="step__check" viewBox="0 0 14 12" aria-hidden="true"><path d="M1.6 6.6l3.5 3.4 7.3-8.2"/></svg><span class="step__name">Review</span></span></button></li>
</ol>
<!-- the line: a faint track, the ink of the steps done and, in the accent, the stretch that is moving.
The three share one path, cut by two clip rectangles; the script draws them -->
<svg class="flow__line" aria-hidden="true" data-line>
<defs>
<clipPath id="flow-track"><rect data-clip="track" x="0" y="-20" width="0" height="60"/></clipPath>
<clipPath id="flow-ink"><rect data-clip="ink" x="0" y="-20" width="0" height="60"/></clipPath>
<clipPath id="flow-run"><rect data-clip="run" x="0" y="-20" width="0" height="60"/></clipPath>
</defs>
<path class="line__track" clip-path="url(#flow-track)" data-path/>
<path class="line__ink" clip-path="url(#flow-ink)" data-path/>
<path class="line__run" clip-path="url(#flow-run)" data-path/>
<g data-ticks></g>
</svg>
<div class="flow__foot">
<p class="flow__note" aria-live="polite" data-note><span class="roll__item">Two items in your bag</span></p>
<div class="flow__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>
</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.
35 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 “Steps with the advancing line” from Animorio and fit it into my siteThe head and the tail of the line are two numbers in steps: 0 is the first tick, 4 the end. The pixels are worked out every frame from the width of the line, so a new window size halfway through a move goes unnoticed by the springs. The line is one SVG path drawn three times: the grey track, the ink and the blue stroke, each cut by a clipPath holding a rectangle, from 0 to the smaller of the two numbers for the ink, between the two for the blue.
The head goes to the chosen step on a stiff spring, HEAD_SPRING; the tail does not look at the step, it looks at the head, on a softer spring, TAIL_SPRING. And the head feels the tail: while the line is stretched, a share PULL of the tail’s force holds it back. The two springs are integrated together in steps of 1/240 of a second, with the frame step capped at 1/30: so the head arrives without a jolt, the tail catches it up, goes a pixel past and comes back.
Names, ticks and checks have no animations of their own: every frame they are worked out from where the head and the tail are, with smoothstep. How done a step is depends on the ink between the middle and the end of its column; how current it is, on the head reaching its tick. From these two numbers come the weight and the width of the variable typeface (font-variation-settings), the shift of the name, the check (stroke-dashoffset) and the colour, mixed in CSS with color-mix between grey, blue and black. That is why going back everything undoes itself, in the same order backwards.
Every stretch between two ticks is a small damped oscillator, eleven times a second. When the tail reaches the tick it was going to, the stretch just filled (or just emptied, going back) gets a blow that grows with the distance travelled, the neighbours half and less. The path is then redrawn with half a sine between the two ticks and a little of the next one; when everything is still it is a straight line again and the loop switches off.
The words of the line and of the button roll: the old ones leave upwards and fade, the new ones rise a beat later. The pill takes the width of the new words with back.out when it grows and with no bounce when it shrinks, and a ResizeObserver keeps it right while the wdth axis widens on hover. 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 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 rewind to the first step. A real click, a touch or a key stops it where it is.
| Name | File | Default | What it changes |
|---|---|---|---|
NOTES | script.js | ['Two items in your bag', …] | 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. |
HEAD_SPRING | script.js | [700, 0.85] | The spring of the head of the line: how stiff it is and how damped (1 means no overshoot). |
TAIL_SPRING | script.js | [130, 0.8] | The spring of the tail, pulled by the head: how stiff and how damped. Softer, the line stretches further. |
PULL | script.js | 0.25 | How much the tail holds the head back while the line is stretched (0 to 1). |
PLUCK | script.js | 2 | How many pixels the line trembles between two ticks where the tail lands. |
WEIGHT | script.js | [360, 540, 680] | The weight of a name: a step to come, the current step, a step done (100 to 900). |
WIDTH | script.js | [96, 106, 100] | The width of the typeface (wdth axis) in the same three cases (62 to 125). |
For the checkout of an online shop, a booking, a sign-up or a long form split into three, four or five steps: wherever you need to say calmly how far along you are, with no icons or circles, and make each step forward feel like something done.
The same animations with the sizes adapted: the four steps stay in a row across the screen, with slightly smaller names; the line of text goes on top and the two buttons underneath, 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, no running and no trembling: the line, the names and the checks are at once in their new place, and the steps and the line come back with a short fade; the words of the line and of the button change with a fade instead of rolling. On opening everything fades in.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 9.3 KB; the libraries: GSAP 28 KB.
Tested on 16 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.