Skip to content
animorio
IT
All components

The bunting button

Buttons, Physics, SVGGSAP, CSS onlyAdvancedCompressed weight: Demo 9.2 KBGSAP 28 KBFreeDownload the zip

The RSVP of a birthday party, Count me in, with a string of cloth pennants hanging slack under the raspberry pill: coral, sun, mint, pink and cream triangles with a line of stitches along the hem. Under the pointer a breeze ruffles them; at a click the string is hoisted in three heaves while the answer goes, then a last pull snaps it taut, the snap runs along the string and every pennant whips. The string is a chain of 36 points worked out every frame and each pennant has its own two swings. A moment later the string is let down again.

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 bunting button</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">
    <!-- the bunting button: the RSVP of a birthday party, with a string of cloth pennants hanging in a slack swag from
         under the pill. Under the pointer a breeze ruffles them; a click hoists the string in a few heaves while the
         answer goes, it snaps taut and every pennant whips and flaps, and the pill says you are on the list. A moment
         later the string is let down again -->
    <div class="bunting" data-bunting>
      <div class="bunting__unit">
        <!-- the string and its pennants, drawn by script.js behind the pill, so the string comes out from under it -->
        <svg class="bunting__art" aria-hidden="true" data-art></svg>
        <button class="bunting__btn" type="button" data-btn data-autoclic>
          <span class="bunting__fill" aria-hidden="true"></span>
          <span class="bunting__faces">
            <span class="bunting__face" data-face="rest">
              <span class="bunting__word" data-wide="Count me in" data-rest-text>Count me in</span>
              <span class="bunting__date" data-rest-date>Sat 8 pm</span>
            </span>
            <span class="bunting__face bunting__face--two" data-face="busy" aria-hidden="true">
              <span class="bunting__small" data-busy-small>Mia turns 30</span>
              <span class="bunting__big" data-busy-text>Sending your RSVP</span>
            </span>
            <span class="bunting__face bunting__face--two" data-face="done" aria-hidden="true">
              <span class="bunting__big" data-done-text>You’re on the list</span>
              <span class="bunting__small" data-done-small>Sat 8 pm · bring a song</span>
            </span>
          </span>
        </button>
      </div>
      <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.

22 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 bunting button” from Animorio and fit it into my site

What it does

On a white background a raspberry pill 60 pixels tall, with a thread of light along the top and a soft shadow under it; on the left Count me in in cream Anybody and beside it the date Sat 8 pm in pink. From under the pill, tied behind its two ends, a raspberry string hangs in a soft swag with seven cloth pennants: coral, sun, mint, pink and cream, each with a line of white stitches along the hem and a light fold down the middle, and a small shadow. Passing over the pill the words widen, the fill swells with a bounce and a breeze ruffles the pennants: they flap to and fro, shortening and darkening as they turn away, and the string sways. At a click the pill says Mia turns 30 and Sending your RSVP, and the string is hoisted in three heaves: at each heave it rises, bounces and the pennants swing; then a last pull snaps it taut, the pill jolts, the snap runs along the string from left to right and every pennant whips in turn. The bunting stays taut just under the pill and the pill says You're on the list · Sat 8 pm · bring a song. A moment later the string is let go and drops back into its swag, swinging. With the keyboard the focus shows a ring and ruffles the pennants; Enter or Space answer. On a phone a tap tugs the string and answers.

How it works

The string: 36 points with Verlet

The string is a chain of 36 points tied behind the pill's two ends, worked out with Verlet integration: each point keeps its speed as the difference between where it is and where it was, gravity pulls it down, a little air holds it back, and twenty passes of constraints put each link back at its length. The links' length comes from how slack the string is (SLACK, its length over the distance between the knots): changing it, the string slackens or tightens by itself, with its own inertia. An SVG behind the pill draws it as a smooth curve of quadratics through the midpoints.

The pennants on the string

Each pennant hangs from three links: its hem follows the string's curve and its tip hangs at right angles to the hem. It has two swings of its own: sideways, a pendulum pushed the other way when the string jerks sideways (θ'' = −(g / l) sin θ − c θ' − a / l); to and fro on its hem, a flap pushed by the string's vertical jolts and by the breeze. The flap shows as perspective: turned away, the pennant looks shorter, darkens and its fold shows more. Each point's acceleration is measured every step from the change in its speed.

Hoisting and snapping

At a click a GSAP timeline shortens the string in three heaves of 0.16 seconds every 0.38: at each heave the string rises and bounces on its own. Then a last pull takes it a hair past its taut length and lets it back: that is the snap. At the same moment each pennant gets a kick of flapping, one after the other 35 thousandths apart, alternating its way, so the snap runs along the string. Going back is the same in reverse: the string lengthens in 0.9 seconds and drops back into its swag, swinging.

The breeze

Under the pointer a breeze rises and falls slowly: on the string it is a sideways push in slow waves along its length, on the pennants two waves of different periods and a different phase for each, so they never flap all together. When the breeze dies and nothing moves any more, GSAP's loop stops drawing.

The states

Three faces on top of each other, as wide as their words (at rest, answering, on the list); the pill is measured once on the widest and the string's drawing takes its width. The button is a real button with aria-disabled while the string is up, an aria-live region that says Sending your RSVP… and then You're on the list. Mia turns 30, Sat 8 pm, bring a song., and an aria-label that changes with the state.

Values to change

NameFileDefaultWhat it changes
LABELscript.jsCount me inThe words at rest.
WHENscript.jsSat 8 pmThe date beside the words, and again underneath once you are on the list.
PARTYscript.jsMia turns 30The small line while the answer goes: whose party it is.
SENDINGscript.jsSending your RSVPThe words while the string is hoisted; the aria-live region says them too.
DONEscript.jsYou’re on the listThe words once the string is taut.
NOTEscript.jsbring a songThe small line under them, after the date: a note for the party.
FLAGSscript.js7How many pennants hang from the string.
SLACKscript.js1.07How slack the string hangs at rest: its length over the distance between the knots. 1.03 nearly taut, 1.14 a deep swag.
HOLDscript.js2.8The seconds the string stays taut before it is let down.
BREEZEscript.js1How hard the breeze under the pointer ruffles the pennants: 0 they stay still.

Try them in the Playground

When to use it

For the RSVP of an invitation to a party, a birthday, a wedding, a dinner with friends or a shop opening, for an event's I'm going or a community's Join the party. The button fakes the answer: in a real site the click sends the answer and the string snaps taut when the server confirms.

On phones

The pill is about 250 pixels wide and the swag hangs some forty pixels under it at most: at 390 pixels it fits. A tap tugs the string, squashes the fill and answers. The pennants keep their size.

With reduced motion

The string does not move and the pennants do not flap: when the answer is confirmed the bunting fades and comes back taut, and when it is let down it fades and comes back in its swag. The words fade in, the pill does not swell and the words do not widen (under the mouse the fill only lightens a little).

Pitfalls

  • With the words going from 80 to 112% width under the pointer, the invisible copy that holds the room for the wide words left a gap of some hundred pixels between Count me in and the date. Now they go from 92 to 106%: you see them widen, and the date stays close.
  • Taut at 1.012 times the distance between the knots, the string vanished almost wholly behind the pill and the pennants at the sides were half hidden. Now taut means 1.02: the bunting keeps a slight curve, just under the edge.

Libraries

GSAP 3.15.0, Anybody (font variabile) 5.3.0.

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

Testing

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