
The elastic button
Buttons, Hover, Text
NewFree
In the minimal style: the rating is a single word, from Poor to Superb, over a hairline with five notches. Sliding along the line, the word takes on weight and width live, on the variable type’s axes, and the weight runs through it one letter after the other; where the grade changes, the letters the two words share slide to their new places and the others swap in a row. Once the grade is picked, the notch lights up blue and the word settles on the line.
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 rating in words that weigh</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/anybody@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- a rating given with one word: a hairline scale with a notch for each grade, and over it the word for the grade
under the pointer, which takes weight and width as the pointer moves along the scale. Where the word changes,
the letters the two words share slide to their new places and the others swap, one after the other. Picked,
the notch lights up and the word settles on the line. Under the scale there are real radio buttons; the
letters and the scale are drawn by the script -->
<form class="weigh" data-weigh onsubmit="return false">
<p class="weigh__ask" id="weigh-ask">How did you sleep?</p>
<fieldset class="weigh__pad" aria-labelledby="weigh-ask" data-pad data-autoclic>
<div class="weigh__word" aria-hidden="true" data-word></div>
<div class="weigh__scale" data-scale>
<svg class="weigh__art" aria-hidden="true" data-art>
<defs>
<clipPath id="weigh-fill"><rect x="0" y="0" width="0" height="40" data-fill/></clipPath>
</defs>
<path class="weigh__line" data-line/>
<path class="weigh__line weigh__line--ink" clip-path="url(#weigh-fill)" data-line-ink/>
<g data-ticks></g>
<line class="weigh__needle" data-needle/>
</svg>
<div class="weigh__choices" data-choices></div>
</div>
</fieldset>
<p class="weigh__note" role="status" aria-live="polite" data-note></p>
</form>
</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.
23 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 rating in words that weigh” from Animorio and fit it into my siteOver the notches lie five real radio buttons, see-through and with no pointer events, in a fieldset tied to the question (aria-labelledby): Tab goes into them, the arrows change the grade, screen readers read the word (aria-label) and the form sends the value. The hand works on the word and the line through pointer events; the arrows move the word at once and log the grade after a 0.55 second pause (a gsap.delayedCall that every key restarts), Enter and Space log it straight away. The keyboard’s ring sits round the notch.
Every letter is a span placed by the script, with its own wght and wdth axes. So as not to ask the page for sizes on every frame, at the start every letter of the words, and every pair of neighbours for the kerning, is measured at 100 pixels on a grid of five weights by five widths (Anybody goes from 100 to 900 and from 50 to 150); any shape in between is read off the grid by bilinear interpolation. The type’s size comes from the same numbers: the widest word along the whole line must fit the box, and on resize the sum is done again.
The place on the line goes from 0 to 4 and is chased by a lightly damped spring. The place is pulled a little towards the nearest notch (a power curve, DETENT), so near a notch the word keeps its own shape and halfway it is exactly the shape in between; from there weight and width are read between THIN and BOLD, NARROW and WIDE. Every letter chases that shape with a first order filter whose time constant grows by RIPPLE seconds for each place away from the side the hand comes from: the weight enters the word like a wave.
When the place crosses halfway between two notches (with a small margin, so the word does not flicker on the border) the two words are lined up by their longest common subsequence: the letters that stay are the same spans, and the jump between their old and new places becomes an offset that a spring takes to zero with one pixel too many. New letters are born in their places under the word’s line and rise with back.out(0.9), one after the other (STAGGER) in the hand’s direction; the old ones stay where they were, rise and fade within 0.15 seconds.
Once the grade is picked, the notch grows and takes the accent with back.out(2.4). When the right word has reached the notch, the letters sink by SETTLE pixels for the heaviest word (a third of that for the lightest) with a one pixel squash, rise back by a third and settle, the middle ones first; at the same moment the line dips under the notch along a gaussian and straightens. Everything runs in a single loop on GSAP’s ticker, with the step capped at 1/30 of a second, which is removed when nothing moves.
A finger slides sideways along the line or across the word (touch-action: pan-y) and the grade falls where it lifts; a mouse button let go far away picks nothing. A click from a script (detail 0, the library’s preview) is a pretend hand in a GSAP timeline: it slides to the grade and picks in turn Superb, Fair, Great and Poor, then puts everything back to rest and starts again.
| Name | File | Default | What it changes |
|---|---|---|---|
LABELS | script.js | ['Poor', 'Fair', 'Good', 'Great', 'Superb'] | The word for each grade, from the lowest to the highest (and how many notches: 2 to 7). |
IDLE | script.js | 'Rate' | The word shown, in grey, before a grade is picked. |
START | script.js | 0 | The grade picked when the page opens (0 = none). |
THIN | script.js | 130 | The weight of the lowest grade’s word (100 to 900). |
BOLD | script.js | 900 | The weight of the highest grade’s word (100 to 900). |
NARROW | script.js | 54 | The width of the lowest grade’s word (50 to 150). |
WIDE | script.js | 140 | The width of the highest grade’s word (50 to 150): the wider it is, the smaller the word gets to fit the box. |
SIZE | script.js | 64 | The largest size of the word, in pixels; in a narrow box it gets smaller. |
DETENT | script.js | 0.6 | How much each notch holds the word in its own shape (0 = the weight changes evenly along the line). |
RIPPLE | script.js | 0.03 | The delay between one letter and the next as the weight runs along the word, in seconds. |
STAGGER | script.js | 0.045 | The time between one letter and the next as they swap, in seconds. |
SETTLE | script.js | 2.5 | How many pixels the heaviest word sinks as it settles on the line. |
To ask how a night in a hotel or a holiday home went, to rate a product after buying it, a dish or a delivery, for the survey at the end of an event: wherever a word says more than five stars and the rating has to sit in one sober line, with the type as the lead.
The line is 300 pixels long at 390 too, and the word gets just small enough for Superb, the widest, to fit the box (about 56 pixels). A finger slides sideways along the line or across the word and the grade falls where it lifts; up and down the page still scrolls (touch-action: pan-y). A tap picks straight away, with the same letter swap and the same settling. The note under it reads Slide or tap along the line.
No sliding, no waves and no settling: the word takes the pointed grade’s shape at once, and when it changes the old one fades and the new one appears in its place; the notch lights up in a fade, the needle and the note change without moving.
GSAP 3.15.0, Anybody (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 11 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.