Skip to content
animorio
IT
All components

The bookmark ribbon button

Buttons, Physics, TextGSAPMediumCompressed weight: Demo 5.0 KBGSAP 28 KBFreeDownload the zip

In the minimal style: the Save for later of a reading app, a thin ink pill on white. Pressed, a thin vermilion ribbon drops out of its bottom edge like the ribbon sewn into the spine of a hardback: it unrolls, swings on its stitch and settles hanging straight, while Saved lands, wide and light first, then narrow and heavy. Pressed again, the ribbon is pulled back up into the pill.

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 bookmark ribbon button</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 bookmark ribbon button: the Save for later of a reading app, a thin ink pill on white. Pressed, a thin
         vermilion ribbon drops out of the bottom edge of the pill like the ribbon of a hardback, swings on its
         stitch and settles; the words become Saved. Pressed again, the ribbon is pulled back up into the pill -->
    <div class="mark" data-mark>
      <div class="mark__wrap">
        <button class="mark__btn" type="button" aria-pressed="false" data-btn data-autoclic>
          <span class="mark__words">
            <span class="mark__word" data-word="rest">Save for later</span>
            <span class="mark__word mark__word--done" data-word="done" aria-hidden="true">Saved</span>
          </span>
        </button>
        <svg class="mark__ribbon" aria-hidden="true" data-ribbon>
          <path class="mark__shade" data-shade></path>
          <path class="mark__tape" data-tape></path>
          <path class="mark__stitch" data-stitch></path>
        </svg>
      </div>
      <p class="mark__note" data-note>Twelve minutes of reading</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>

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.

18 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 bookmark ribbon button” from Animorio and fit it into my site

What it does

On a white background a thin pill, 60 pixels tall, with just an ink outline and the words Save for later inside in Archivo; below it, on the left, small and grey, the line Twelve minutes of reading. Under the mouse the words widen slightly and the tip of a vermilion ribbon peeks a few pixels out of the bottom edge, towards the right, swaying. Pressed, the pill gives a little and springs back; the words narrow and lift out, and the ribbon drops out of the pill like the bookmark of a hardback: it unrolls, falls a few pixels too far and springs back up, then swings on its stitch, its lower part lagging like cloth, until it hangs straight. Its end is cut in a swallowtail. Meanwhile Saved lands, wide and light first, then narrow and heavy, a pixel too low and back, and the line below becomes In your reading list. Moving the mouse across the saved pill sways the ribbon. Pressed again, the ribbon gives a little tug the other way and is pulled back up into the pill, and Save for later returns.

How it works

A damped pendulum

The ribbon has three numbers: its length, its angle and how fast the angle changes. Every frame a spring pulls the angle back to zero (STIFF) and damping eats its speed (DAMP), with the time step capped at 1/30 of a second. When angle and speed are almost zero and no tween is moving the ribbon, the loop takes itself off GSAP’s ticker and nothing is drawn any more.

Cloth that lags behind

The ribbon is a centre line of ten pieces: each piece has the pendulum’s angle plus a bend that grows with the square of the distance from the stitch, against the speed. So while it swings the lower part lags behind and the ribbon curves like cloth. The two edges sit half a width either side of the line, and at the end the swallowtail cut goes back NOTCH pixels.

The ribbon that drops

GSAP moves the length: it falls on an In curve to four pixels past LENGTH, and the moment it stops the pendulum gets a kick of speed; then the length springs back to LENGTH on an elastic. Pulling it up, the kick goes the other way and the length goes back to zero on a harder In curve.

The words that land

Archivo has width and weight axes. The old words leave narrowing and rising; the new ones start 116 wide and 320 heavy, rise from below to a pixel under their place and settle, while width and weight go to 92 and 640.

Accessible

It is a real button with aria-pressed, which says whether the piece is saved; an aria-live line announces the new state; keyboard focus has its ring set off the edge. The ribbon is a decorative SVG with aria-hidden.

Values to change

NameFileDefaultWhat it changes
LABELscript.jsSave for laterThe words at rest.
DONEscript.jsSavedThe words once the piece is saved.
NOTEscript.jsTwelve minutes of readingThe small line under the pill.
NOTE_DONEscript.jsIn your reading listThe small line once the piece is saved.
LENGTHscript.js46How far the ribbon hangs below the pill, in pixels.
WIDTHscript.js12The width of the ribbon, in pixels.
SWINGscript.js1How hard the ribbon swings when it drops; 0 keeps it still.
STIFFscript.js34The spring that brings the ribbon back to hanging straight.
DAMPscript.js3.2How quickly the swinging dies away.

Try them in the Playground

When to use it

For a Save button that should feel like a gesture: saving an article for later on a blog or a magazine, putting a book on a reading list, bookmarking a recipe.

On phones

The pill narrows to 32 pixels from the edges and the ribbon finds its place on the real width. A tap presses the pill like a click; the tip peeking on hover and the ribbon swaying with the mouse are only there with a mouse.

With reduced motion

No swinging and no bounces: the ribbon fades in and out already at its length, and the words swap with a fade.

Pitfalls

  • Inside the pill the ribbon would be cut by the hidden overflow: it lives in a sibling SVG under the pill, so the pill’s edge covers the stitch and the ribbon seems to come out from under it.
  • The small centred line ended up under the hanging ribbon: it sits on the left, with a maximum width that keeps the ribbon’s place clear.
  • Two quick clicks would overlap the drop and the pull: clicks are ignored while the timeline runs.
  • The pendulum must not run forever: it takes itself off the ticker once still and wakes only on a kick or a tween of the length.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 5.0 KB; the libraries: GSAP 28 KB.

Testing

Tested on 22 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