
The send button
Buttons, Transitions, SVG
NewFree
A shop’s alerts, delivered like post, in the rich style: a painted metal mailbox on its wooden post. When an alert arrives the red flag springs up, the lid swings open on its hinge, an envelope with a stamp and a postmark comes out of the box, flies to its place, turns over and opens, and the letter inside is the alert. Four kinds, each with its own envelope: shipped, payment failed (the box shakes and the envelope arrives torn), new message, saved.
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 mailbox alert</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/caveat@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the alerts of a small shop, delivered like post: a painted metal mailbox on its wooden post. When an alert
arrives the red flag springs up, the lid swings open on its hinge, an envelope (paper grain, a perforated
stamp, a postmark) rises out of the box, flies to its place and opens, and the letter inside is the alert.
A failed payment shakes the box and the envelope arrives with a torn corner. The alert closes by itself after
a few seconds, or with its cross or Esc, and the letter goes back into the envelope and the box -->
<section class="post" aria-label="Shop alerts" data-post>
<div class="post__scene" data-scene>
<svg class="box" viewBox="0 0 200 240" aria-hidden="true" data-box>
<defs>
<linearGradient id="mb-paint" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" class="stop-paint-dark"/>
<stop offset="0.18" class="stop-paint-light"/>
<stop offset="0.55" class="stop-paint"/>
<stop offset="1" class="stop-paint-dark"/>
</linearGradient>
<linearGradient id="mb-lid" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" class="stop-paint-light"/>
<stop offset="1" class="stop-paint-dark"/>
</linearGradient>
<linearGradient id="mb-wood" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" class="stop-wood-dark"/>
<stop offset="0.35" class="stop-wood-light"/>
<stop offset="1" class="stop-wood-dark"/>
</linearGradient>
<linearGradient id="mb-brass" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" class="stop-brass-light"/>
<stop offset="0.5" class="stop-brass"/>
<stop offset="1" class="stop-brass-dark"/>
</linearGradient>
<linearGradient id="mb-flag" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" class="stop-flag-light"/>
<stop offset="1" class="stop-flag"/>
</linearGradient>
<linearGradient id="mb-steel" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" class="stop-steel-dark"/>
<stop offset="0.5" class="stop-steel-light"/>
<stop offset="1" class="stop-steel-dark"/>
</linearGradient>
<radialGradient id="mb-rivet" cx="0.35" cy="0.3" r="0.8">
<stop offset="0" class="stop-steel-light"/>
<stop offset="1" class="stop-steel-dark"/>
</radialGradient>
<filter id="mb-soft" x="-30%" y="-200%" width="160%" height="500%">
<feGaussianBlur stdDeviation="4"/>
</filter>
</defs>
<!-- the shadow on the ground -->
<ellipse class="box__ground" cx="100" cy="232" rx="66" ry="6" filter="url(#mb-soft)"/>
<!-- the wooden post, its grain and the bracket the box sits on -->
<g class="box__post">
<rect x="89" y="148" width="22" height="88" rx="2" fill="url(#mb-wood)"/>
<path class="box__grain" d="M95 152 C 94 172, 97 190, 95 212 S 96 228, 95 236"/>
<path class="box__grain" d="M103 150 C 104 168, 101 186, 104 204 S 103 224, 105 236"/>
<path class="box__grain box__grain--knot" d="M99 196 c 3 -3 5 1 2 4 c -3 2 -5 -1 -2 -4 z"/>
<rect x="62" y="146" width="76" height="9" rx="2" fill="url(#mb-wood)"/>
<rect x="62" y="153" width="76" height="2" class="box__bracket-shade"/>
<circle cx="70" cy="150.5" r="1.6" fill="url(#mb-rivet)"/>
<circle cx="130" cy="150.5" r="1.6" fill="url(#mb-rivet)"/>
</g>
<!-- the box: the shake of a failed payment turns it about the foot of the post -->
<g class="box__body" data-body>
<!-- the dark slot under the lid, seen when it opens -->
<rect class="box__mouth" x="48" y="56" width="104" height="9" rx="3"/>
<rect x="40" y="60" width="120" height="90" rx="12" fill="url(#mb-paint)"/>
<rect class="box__bevel" x="44.5" y="64.5" width="111" height="81" rx="9"/>
<path class="box__band" d="M40 132 h120 v6 a12 12 0 0 1 -12 12 h-96 a12 12 0 0 1 -12 -12 z"/>
<path class="box__shine" d="M50 70 C 49 92, 49 118, 52 140" />
<path class="box__shine box__shine--thin" d="M57 72 C 56 92, 56 112, 58 128" />
<!-- two soft dents, a light rim and a shadow each -->
<ellipse class="box__dent-shade" cx="132" cy="98" rx="7" ry="4.5" transform="rotate(-18 132 98)"/>
<ellipse class="box__dent-light" cx="130.5" cy="96" rx="5.5" ry="3" transform="rotate(-18 130.5 96)"/>
<ellipse class="box__dent-shade" cx="62" cy="128" rx="5" ry="3" transform="rotate(12 62 128)"/>
<ellipse class="box__dent-light" cx="61" cy="126.6" rx="4" ry="2" transform="rotate(12 61 126.6)"/>
<!-- the brass plate -->
<rect x="70" y="102" width="60" height="18" rx="3" fill="url(#mb-brass)"/>
<rect class="box__plate-edge" x="71.5" y="103.5" width="57" height="15" rx="2"/>
<text class="box__plate-word" x="100" y="114.6" text-anchor="middle">LETTERS</text>
<circle cx="74.5" cy="111" r="1.4" fill="url(#mb-rivet)"/>
<circle cx="125.5" cy="111" r="1.4" fill="url(#mb-rivet)"/>
<!-- rivets on the corners of the front panel -->
<circle cx="49" cy="73" r="1.9" fill="url(#mb-rivet)"/>
<circle cx="151" cy="73" r="1.9" fill="url(#mb-rivet)"/>
<circle cx="49" cy="141" r="1.9" fill="url(#mb-rivet)"/>
<circle cx="151" cy="141" r="1.9" fill="url(#mb-rivet)"/>
<!-- the lid, hinged on its left pin -->
<g class="box__lid" data-lid>
<rect x="35" y="50" width="130" height="14" rx="5" fill="url(#mb-lid)"/>
<rect class="box__lid-lip" x="35" y="60" width="130" height="4" rx="2"/>
<path class="box__lid-shine" d="M44 53.5 H 150"/>
<rect x="150" y="56" width="10" height="5" rx="1.5" fill="url(#mb-steel)"/>
</g>
<rect x="31" y="53" width="9" height="9" rx="2" fill="url(#mb-steel)"/>
<circle cx="35.5" cy="57.5" r="1.7" fill="url(#mb-rivet)"/>
<!-- the flag, pivoting on a pin on the right side of the box -->
<g class="box__flag" data-flag>
<rect x="164.5" y="54" width="5" height="70" rx="2.5" fill="url(#mb-steel)"/>
<path d="M169 54 C 180 51, 188 57, 197 54 L 197 74 C 188 77, 180 71, 169 74 Z" fill="url(#mb-flag)"/>
<path class="box__flag-fold" d="M180 53.5 C 181 60, 181 66, 180 72.5"/>
<path class="box__flag-shine" d="M172 58 C 180 56, 186 60, 193 58"/>
</g>
<circle class="box__pin" cx="167" cy="120" r="3.6" fill="url(#mb-rivet)"/>
<circle class="box__pin-dot" cx="167" cy="120" r="1.1"/>
<!-- the point the envelope comes out of -->
<rect class="box__mark" x="99" y="60" width="2" height="2" data-mouth/>
</g>
</svg>
<!-- the envelope is two layers with the letter between them: the far sheet (and the flap once it is open)
behind the alert, the near side (the address side while it flies, the back with its seams and closed
flap once it has turned) in front of it -->
<div class="env env--far" aria-hidden="true" data-env-far>
<div class="env__sheet"></div>
<div class="env__flap-open" data-flap-open></div>
</div>
<!-- the alert itself: the letter. Its role and words change with each kind -->
<div class="toast" data-toast>
<div class="toast__sheet">
<span class="toast__icon" data-icon aria-hidden="true"></span>
<span class="toast__text">
<span class="toast__title" data-title>Order shipped</span>
<span class="toast__body" data-body-text>Two linen shirts are on their way.</span>
</span>
<button class="toast__close" type="button" aria-label="Dismiss alert" data-close>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 7l10 10M17 7L7 17"/></svg>
</button>
</div>
</div>
<div class="env env--near" aria-hidden="true" data-env-near>
<div class="env__face env__face--front" data-face-front>
<span class="env__from">Larkspur Goods<br>Mill Lane</span>
<span class="env__to" data-to>Ada Moreau</span>
<span class="env__lines"></span>
<svg class="env__stamp" viewBox="0 0 36 42">
<defs>
<mask id="mb-perf" maskUnits="userSpaceOnUse" x="0" y="0" width="36" height="42">
<rect x="0" y="0" width="36" height="42" fill="#fff"/>
<g fill="#000">
<circle cx="3" cy="0" r="1.7"/><circle cx="9" cy="0" r="1.7"/><circle cx="15" cy="0" r="1.7"/><circle cx="21" cy="0" r="1.7"/><circle cx="27" cy="0" r="1.7"/><circle cx="33" cy="0" r="1.7"/>
<circle cx="3" cy="42" r="1.7"/><circle cx="9" cy="42" r="1.7"/><circle cx="15" cy="42" r="1.7"/><circle cx="21" cy="42" r="1.7"/><circle cx="27" cy="42" r="1.7"/><circle cx="33" cy="42" r="1.7"/>
<circle cx="0" cy="3" r="1.7"/><circle cx="0" cy="9" r="1.7"/><circle cx="0" cy="15" r="1.7"/><circle cx="0" cy="21" r="1.7"/><circle cx="0" cy="27" r="1.7"/><circle cx="0" cy="33" r="1.7"/><circle cx="0" cy="39" r="1.7"/>
<circle cx="36" cy="3" r="1.7"/><circle cx="36" cy="9" r="1.7"/><circle cx="36" cy="15" r="1.7"/><circle cx="36" cy="21" r="1.7"/><circle cx="36" cy="27" r="1.7"/><circle cx="36" cy="33" r="1.7"/><circle cx="36" cy="39" r="1.7"/>
</g>
</mask>
</defs>
<g mask="url(#mb-perf)">
<rect class="stamp__paper" x="0" y="0" width="36" height="42"/>
<rect class="stamp__field" x="4" y="4" width="28" height="34" rx="1"/>
<!-- a larkspur: a stem with its little spurred flowers and two leaves -->
<path class="stamp__stem" d="M18 35 C 18 27, 17.5 20, 18.5 10"/>
<path class="stamp__leaf" d="M18 30 C 13 28, 11 25, 11 23 C 14 24, 17 26, 18 30 Z"/>
<path class="stamp__leaf" d="M18.2 27 C 23 25, 25 22, 25.5 20 C 22 21, 19 23.5, 18.2 27 Z"/>
<path class="stamp__bloom" d="M18.5 10 c -3 -1 -3 -4 0 -4 c 3 0 3 3 0 4 z M17 15 c -3 0 -4 -3 -1 -4 c 2 0 3 2 1 4 z M20 19 c 3 0 4 -3 1 -4 c -2 0 -3 2 -1 4 z M17 23 c -3 0 -4 -3 -1 -3.5 c 2 0 2.5 2 1 3.5 z"/>
</g>
</svg>
<svg class="env__postmark" viewBox="0 0 64 40">
<circle cx="20" cy="20" r="15"/>
<circle cx="20" cy="20" r="11"/>
<path d="M38 12 c 4 -3 8 3 12 0 s 8 3 12 0 M38 20 c 4 -3 8 3 12 0 s 8 3 12 0 M38 28 c 4 -3 8 3 12 0 s 8 3 12 0"/>
<text x="20" y="22.6" text-anchor="middle">LARK</text>
</svg>
</div>
<div class="env__face env__face--back" data-face-back>
<svg class="env__seams" viewBox="0 0 100 50" preserveAspectRatio="none">
<path d="M0 50 L 42 27 M100 50 L 58 27"/>
</svg>
<div class="env__flap-shut" data-flap-shut></div>
</div>
<div class="env__scrap" data-scrap></div>
</div>
</div>
<!-- the buttons that send each kind of alert -->
<div class="post__send" role="group" aria-label="Send an alert">
<button class="chip" type="button" data-send="shipped">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 8.5 12 4l8 4.5v7L12 20l-8-4.5z M4 8.5 12 13l8-4.5 M12 13v7"/></svg>
<span>Shipped</span>
</button>
<button class="chip" type="button" data-send="failed">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 7h17v10h-17z M3.5 10.5h17 M7 14.5h4 M15 13l4 4 M19 13l-4 4"/></svg>
<span>Failed</span>
</button>
<button class="chip" type="button" data-send="message">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 6.5h15v9h-8l-4 3.5v-3.5h-3z"/></svg>
<span>Message</span>
</button>
<button class="chip" type="button" data-send="saved">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 4.5h10v15l-5-3.6-5 3.6z"/></svg>
<span>Saved</span>
</button>
</div>
<!-- what screen readers hear: the calm kinds politely, the failed payment at once -->
<p class="sr" role="status" aria-live="polite" data-live-status></p>
<p class="sr" role="alert" aria-live="assertive" data-live-alert></p>
</section>
</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.
58 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 mailbox alert” from Animorio and fit it into my siteThe mailbox is an SVG: the flag, the lid and the whole box are groups GSAP turns with svgOrigin, that is about a point in the drawing's coordinates: the flag about the pin on the side, the lid about its hinge, the box about the foot of the post when it shakes. So every part stays fixed where it is attached, at any size.
The envelope is made of two layers moving together: behind, the far sheet (and the flap once open); in front, the near side (the address while it flies, the back with its seams and the closed flap once it has turned). The alert sits between them, so it really comes out of the pocket: the near side covers it until it rises over the edge.
From the mouth of the box to its place the envelope follows a curve: one variable from 0 to 1 tweened by GSAP, and at every step x linear, y with a sine that lifts it, the scale with an ease that makes it grow early and a rotation that leans in the middle of the flight and straightens on arrival.
The envelope turns over by squeezing to zero on the x axis and widening again with a back.out: the faces are swapped at the moment it is as thin as a thread. The closed flap shortens towards the edge (scaleY from the top), and once it is gone the open one grows, behind the letter, from the edge upwards.
Each kind changes the colour of the envelope and the letter (a CSS variable taken from :root), the SVG icon and the words; the failed payment adds the shake, the torn corner (a zig-zag clip-path) and the falling scrap. The words go to a polite role="status" line, or to a role="alert" one that interrupts when the payment has failed.
| Name | File | Default | What it changes |
|---|---|---|---|
FLAG_SPRING | script.js | 0.42 | How springy the flag is when it pops up: 0.2 stiff, 0.8 wobbly. |
FLIGHT | script.js | 0.75 | The seconds the envelope takes to fly from the box to its place. |
OPEN | script.js | 0.5 | The seconds the envelope takes to turn over and open. |
HOLD | script.js | 2.8 | The seconds an alert stays before it goes back into the box by itself. |
GAP | script.js | 0.7 | The seconds between two alerts when the demo plays by itself. |
SHAKE | script.js | 3.5 | How far the box shakes for a failed payment, in degrees. |
FIRST | script.js | 0.6 | The seconds before the first alert arrives. |
MESSAGES | script.js | quattro avvisi: Order shipped, New message, Payment failed, Saved | The alerts, in turn: the kind, the title, the line and the name on the envelope. |
For the alerts of an online shop, a shipping or mail app, a service with a handmade touch: order shipped, payment failed, new message, saved. One alert at a time, with a title and a line; the delivery takes about three seconds, so it suits places where alerts do not come in bursts.
On a phone the alert goes to the top, almost as wide as the box, and the smaller mailbox sits under it on the left; the envelope, the flap and the buttons shrink, so it all fits a 341 by 410 box. The animations are the same.
With reduced motion no envelopes fly: the flag is already up and the alert fades in at its place, and fades out the same way.
GSAP 3.15.0, Instrument Sans (font variabile) 5.3.0, Caveat (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 22 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.