
The elastic button
Buttons, Hover, Text
NewFree
In the minimal style, on white and made only of type: the Read the essay of a small review, set on three ragged lines, one left, one right and one centred, like a typesetting proof. Under the pointer the lines slide and justify into one perfect block: every word widens on the width axis to the same measure, two margin hairlines rise at the sides and the pill tightens round the block. Pressed, the block closes into a single line and a vermilion arrow steps forward.
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 button that justifies itself</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 button that justifies itself: the Read the essay of a small review, on white. The words sit on three
ragged lines, set left, right and centre like a typesetting proof. Under the pointer the three lines slide
and justify into one perfect block, every word widened on the width axis to the same measure, and the pill
tightens round it. Pressed, the block closes into one line and a drawn arrow steps forward -->
<div class="just" data-just>
<p class="just__brand">Ostrom Review</p>
<button class="just__btn" type="button" data-btn data-autoclic aria-label="Read the essay">
<span class="just__guide just__guide--l" data-guide aria-hidden="true"></span>
<span class="just__guide just__guide--r" data-guide aria-hidden="true"></span>
<span class="just__stack" data-stack aria-hidden="true"></span>
<span class="just__single" data-single aria-hidden="true">
<span class="just__row" data-row></span>
<svg class="just__arrow" viewBox="0 0 24 14" data-arrow><path d="M1.5 7h19M15 1.8 20.6 7 15 12.2" data-arrow-path></path></svg>
</span>
</button>
<p class="just__note" data-note>Autumn issue · 12 min read</p>
<p class="sr" aria-live="polite" data-live></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>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.
16 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 button that justifies itself like a column of text” from Animorio and fit it into my siteOnce the typeface has arrived, the script measures every word at eight points of Archivo’s width axis, from 62 to 125, with a hidden element. So to give a word any width it is enough to read the table backwards: the axis value is found between two nearby measures, in proportion.
The measure of the block is the longest word times MEASURE. A single number, p, goes from 0 (ragged) to 1 (justified): for each line the wanted width is its natural one plus (measure − natural) times p. The axis gives what it can; what is missing becomes space between the letters, divided over the letters minus one, so the last letter ends right on the margin. The position too goes from the ragged one to the left margin with p.
GSAP takes p to 1 with back.out(1.7): it goes a little over, the words widen for a moment past the margin and come back. With p the two margin hairlines rise too, on a vertical scale, and the pill tightens by SHRINK pixels.
On click the lines move in towards the middle one and vanish, while GSAP takes the pill from the block’s size to the single line’s with back.out; the three words of the line come in one after the other, the arrow draws itself with the dash offset and steps forward and back. After HOLD seconds everything comes back.
It is a real button, with the label Read the essay read out whole; an aria-live line says Opening the essay. With reduced motion the lines are justified at once under the pointer and on click the two forms swap with a fade.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Read|the|essay | The words of the button, one per line, split by |. |
NOTE | script.js | Autumn issue · 12 min read | The small line under the pill. |
NOTE_DONE | script.js | Opening the essay | The small line while the essay opens. |
ALIGN | script.js | left|right|center | How the ragged lines are set at rest, one per line. |
MEASURE | script.js | 1.16 | How wide the justified block is, compared with the longest word. |
JUSTIFY | script.js | 0.6 | The seconds the lines take to justify. |
SHRINK | script.js | 5 | How much the pill tightens round the block, in pixels. |
HOLD | script.js | 1.3 | The seconds the single line stays before coming back. |
For the button that opens a long text: the Read of an article or an essay, a blog’s Continue, a magazine’s index, the page of a publisher or a type foundry.
The measures are worked out again on the size of the type, which follows the width and height of the screen; a tap runs the whole cycle, justification included, because without a mouse there is no hover.
The lines are justified at once under the pointer, with no spring; on click the block and the single line swap with a fade.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 5.3 KB; the libraries: GSAP 28 KB.
Tested on 24 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.