Skip to content
animorio
IT
All components

The menu whose links stretch between the margins

Menus, Text, HoverGSAP, CSS onlyMediumCompressed weight: Demo 6.8 KBGSAP 28 KBFreeDownload the zip

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.

The code

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>

Playground

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

Or ask Claude Code for it

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 site

What it does

On black there is the name of an invented bookshop, Orvel Books, and a thin pill button with the word Menu, which widens a little when you pass over it. Pressed, Menu goes up and Close comes in its place, and two very thin margins are drawn from the top, left and right of the column, like the edges of a printed page. The five links rise one after the other from their masks, Books, Authors, Readings, Journal and Visit, in capitals, light and narrow, all on the left; Books, the page you are on, is already a little wider. Passing the pointer over a link, its letters move apart on a spring, take weight and width, until the word takes the whole line from one margin to the other; the spring carries it a little further, and the word pushes the two margins out by a few pixels: there the margins curve and turn vermilion, then settle. The other links let go and go back short. Clicking a link, it stretches once more, stays vermilion for a moment and the menu closes: the links sink into their masks from the last one, the margins roll back up and Close turns back into Menu.

How it works

One spring per link

Each 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.

Letters that fill the line

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.

Margins that give way

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.

Opening, closing, choosing

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.

Keys and phone

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.

Values to change

NameFileDefaultWhat it changes
SPRINGscript.js[190, 15]The spring of the stretching link: how stiff and how much it is held back (less, more bounce).
RESTscript.js0.16How far the page you are on stays stretched when nothing is pointed at.
WEIGHTscript.js[300, 640]The weight of a short link and of a stretched one.
WIDTHscript.js[100, 112]The width axis of a short link and of a stretched one.
PUSHscript.js3How many pixels a stretched link pushes the margins out.
STAGGERscript.js0.05The seconds between one link and the next when opening and closing.

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

No springs and no bounces: the link under the pointer is stretched at once, the links and the margins fade in and out.

Pitfalls

  • A word's natural width changes with the weight and the width axis: it is measured once at the two extremes (and on resize) and interpolated in between, instead of measuring every frame, which would force the browser to lay out the page again.
  • letter-spacing also adds space after the last letter, and the word would not reach the margin: the last letter has zero spacing.
  • The preview's pilot clicks every 2.8 seconds: clicks without detail (detail 0) start the tour only when the menu is closed, and are ignored during the tour.

Libraries

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.

Testing

Tested on 24 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components