
The elastic button
Buttons, Hover, Text
NewFree
In the minimal style, on black: the menu of a small bookshop. Between two hairline margins, like the edges of a printed column, the links sit short and light on the left; the one under the pointer spreads its letters on a spring, takes weight and width until it fills the line from margin to margin and, going a little past, pushes both margins out by a few pixels, which turn vermilion there.
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 menu whose links stretch between the margins</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the menu of a small bookshop. Between two hairline margins, like the edges of a printed column, the links sit
short on the left. The one under the pointer stretches its letters until it touches both margins and pushes
them out by a pixel; the margins bulge there in vermilion. The page you are on stays a little stretched. -->
<div class="column" data-frame>
<div class="top">
<span class="brand">Orvel Books</span>
<button class="toggle" type="button" aria-expanded="false" aria-controls="orvel-menu" data-toggle data-autoclic>
<span class="toggle__words" aria-hidden="true">
<span class="toggle__word" data-word-open>Menu</span>
<span class="toggle__word" data-word-close>Close</span>
</span>
<span class="sr" data-toggle-name>Open the menu</span>
</button>
</div>
<div class="field" data-field>
<svg class="margins" aria-hidden="true" data-margins>
<path class="margin" data-margin-left></path>
<path class="margin" data-margin-right></path>
<path class="margin margin--hot" data-hot-left></path>
<path class="margin margin--hot" data-hot-right></path>
</svg>
<nav class="menu" id="orvel-menu" aria-label="Main" data-menu inert>
<ul class="menu__list">
<li><a class="row" href="#books" data-desc="New and second-hand, two floors" aria-current="page" data-row><span class="row__mask"><span class="row__word" data-word>Books</span></span></a></li>
<li><a class="row" href="#authors" data-desc="Forty writers who read here" data-row><span class="row__mask"><span class="row__word" data-word>Authors</span></span></a></li>
<li><a class="row" href="#readings" data-desc="Thursdays at seven" data-row><span class="row__mask"><span class="row__word" data-word>Readings</span></span></a></li>
<li><a class="row" href="#journal" data-desc="Notes from the counter" data-row><span class="row__mask"><span class="row__word" data-word>Journal</span></span></a></li>
<li><a class="row" href="#visit" data-desc="12 Canal Row, open daily" data-row><span class="row__mask"><span class="row__word" data-word>Visit</span></span></a></li>
</ul>
</nav>
</div>
<p class="note" data-note><span class="note__line" data-note-line>Point at a link, it fills the line</span></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.
24 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 menu whose links stretch between the margins” from Animorio and fit it into my siteEach link has a number s between 0 (short) and 1 (stretched), following its goal on a spring (SPRING): 1 for the link under the pointer, REST for the page you are on when nothing is pointed at, 0 for the others. The spring is lightly damped, so the link goes a little past 1 and comes back.
Each word is split into letters. On load and on resize its width is measured at the lowest and highest weight and width axis (WEIGHT, WIDTH). Every frame the spacing between letters is s times the difference between the column and the natural width, divided by the number of gaps: with s at 1 the word measures exactly the column, past 1 it goes slightly over. The last letter has no spacing, so the word ends on the margin.
The two margins are two SVG paths redrawn every frame, a point every 3 pixels: each link stretched past 0.78 pushes them out with a bell a little narrower than a row, up to PUSH pixels. A second vermilion path covers only the stretch pushed by the most stretched link, brighter the harder it pushes.
GSAP: Menu and Close swap, the margins are drawn from the top in 0.8 seconds, the links rise from their masks with back.out STAGGER seconds apart. Choosing a link its spring gets an extra push, the link goes from vermilion back to white, and after three quarters of a second the menu closes: the links sink from the last one and the margins withdraw.
The button has aria-expanded and the closed menu is inert; the arrows move the stretched link, Esc closes, Tab goes round the button and the links. On the phone a finger sliding over the links works like the pointer (the field has touch-action none only while the menu is open), and a tap chooses.
| Name | File | Default | What it changes |
|---|---|---|---|
SPRING | script.js | [190, 15] | The spring of the stretching link: how stiff and how much it is held back (less, more bounce). |
REST | script.js | 0.16 | How far the page you are on stays stretched when nothing is pointed at. |
WEIGHT | script.js | [300, 640] | The weight of a short link and of a stretched one. |
WIDTH | script.js | [100, 112] | The width axis of a short link and of a stretched one. |
PUSH | script.js | 3 | How many pixels a stretched link pushes the margins out. |
STAGGER | script.js | 0.05 | The seconds between one link and the next when opening and closing. |
For the menu of anyone who works with words: a bookshop, a publisher, a magazine, a type foundry, a copywriting studio. It suits four, five or six short links on a dark or light background; long words want a slightly wider column.
The column is as wide as the screen less 32 pixels and at most 88% of the height tall; the links shrink with the width and the height. A finger sliding over the links stretches them like the pointer, and a tap chooses.
No springs and no bounces: the link under the pointer is stretched at once, the links and the margins fade in and out.
GSAP 3.15.0, Mona Sans (font variabile, con gli assi della larghezza e del peso) 5.3.0.
Compressed with gzip, the three demo files weigh 6.8 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.