
The send button
Buttons, Transitions, SVG
NewFree
A coffee roastery’s wholesale orders, with a filter where every state is a pocket. Changing the filter, the new orders come out of their pocket and open in their places, between the rows already there, which move aside; then every order that no longer belongs closes in on its status label, the label flies into its pocket, slips behind the edge and the pocket takes it with a move of its own (the flame flares, the bag puffs up, the van bounces), and the table tightens. Never empty, still in under a second. Opened, an order shows the roast curve of its batch, drawn in the colour of the beans at every temperature.
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 table with pockets</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/standard.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the table with pockets: a coffee roastery's wholesale orders. Every status tab is a pocket: filtering, the
orders that do not belong close in on their status label and the label flies into its pocket; choosing a
pocket, its orders come out and open up in their places. An order opens on the roast curve of its batch -->
<section class="orders" aria-labelledby="orders-title" data-orders data-autoclic>
<header class="orders__head">
<h2 class="orders__title" id="orders-title">Wholesale orders</h2>
<p class="orders__note">Roasted this week</p>
</header>
<div class="tabs" role="group" aria-label="Show orders" data-tabs>
<span class="tabs__knob" aria-hidden="true" data-knob></span>
<button class="tab" type="button" aria-pressed="true" data-filter="all">
<span class="tab__in">
<span class="tab__label">All</span>
<span class="tab__count" data-count></span>
</span>
</button>
<button class="tab tab--roasting" type="button" aria-pressed="false" data-filter="roasting">
<span class="tab__in">
<span class="tab__icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 3.2c1.3 3.4 5.8 5.6 5.8 11a5.8 5.8 0 0 1-11.6 0c0-2.8 1.5-4.6 2.9-5.8.2 1.8.9 2.8 1.9 3.2-.5-3 .1-5.8 1-8.4z"/><path d="M12 20.2a2.5 2.5 0 0 1-2.5-2.5c0-1.5 1.2-2.4 2-3.6.4 1.1 1.1 1.7 1.8 2.1.7.4 1.2 1 1.2 1.6a2.5 2.5 0 0 1-2.5 2.4z"/></svg></span>
<span class="tab__label">Roasting</span>
<span class="tab__count" data-count></span>
</span>
</button>
<button class="tab tab--packed" type="button" aria-pressed="false" data-filter="packed">
<span class="tab__in">
<span class="tab__icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M6.3 8.6h11.4v10.6a1.4 1.4 0 0 1-1.4 1.4H7.7a1.4 1.4 0 0 1-1.4-1.4z"/><path d="M6.9 8.6 7.8 3.8h8.4l.9 4.8"/><path d="M10 6.2h4"/><circle cx="12" cy="13.6" r="1.9"/></svg></span>
<span class="tab__label">Packed</span>
<span class="tab__count" data-count></span>
</span>
</button>
<button class="tab tab--shipped" type="button" aria-pressed="false" data-filter="shipped">
<span class="tab__in">
<span class="tab__icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M2.8 6.4h11v10.2h-11z"/><path d="M13.8 9.6h3.9l3.5 3.6v3.4h-7.4"/><circle cx="7" cy="17.4" r="1.9"/><circle cx="17.2" cy="17.4" r="1.9"/></svg></span>
<span class="tab__label">Shipped</span>
<span class="tab__count" data-count></span>
</span>
</button>
</div>
<div class="sheet" data-sheet>
<table class="table" aria-labelledby="orders-title" data-table>
<thead>
<tr>
<th class="c-cafe" scope="col">Café</th>
<th class="c-coffee" scope="col">Coffee</th>
<th class="c-kg num" scope="col">Weight</th>
<th class="c-total num" scope="col">Total</th>
<th class="c-state" scope="col">Status</th>
</tr>
</thead>
<tbody data-rows></tbody>
</table>
</div>
<div class="flight" aria-hidden="true" data-flight></div>
<p class="sr" aria-live="polite" data-live></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.
114 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 table with pockets” from Animorio and fit it into my siteEvery order has a place in the table: row (its real row), wait (the place kept, unseen, while its label comes), hold (the place kept while the window closes), free (the place empty, the label has gone) and none (in its pocket). When the filter changes the table goes through the union of both lists, in the order of the data: the places of the orders coming appear, unseen (visibility hidden), between the rows already there, which slide to make room with a transform on the tr. The orders coming leave their pocket at once; the ones leaving stay real rows until the last new one is about to land (and never past the moment the first one starts to open, so there is always a row to read). Then, once, the empty places go and the rows move up. The two flocks never fly together.
A row leaving is replaced, in the same frame, by a copy (the row cloned into a table with the same columns, read from the heads into a colgroup, and its roast if it was open) inside a window: a div with overflow hidden whose edges go from the row’s to the label’s in CLOSE seconds, while the copy fades and the background takes the label’s tint. The label flies along a cubic Bézier with its first control point straight above the start and the second straight below the pocket (SWING of the climb): it leaves and arrives vertically, tilts up to 8 degrees towards where it goes, lifts towards us up to 28% bigger with its shadow, in FLY seconds (more or less with the distance), speeding up towards the pocket and slowing down towards the row.
The layer of the labels in flight is above the table and below the pockets (z-index): the label passes behind the track’s bottom edge and vanishes there, with no fade. The pocket takes it: its insides jump 3 pixels with a back.out in 0.22 seconds (a filter is used many times in a row), the count swells, and the icon makes its move, the flame stretches up from its base, the bag squashes, the van jolts tilting on its back wheels. The moves are on the svg, the button’s press (scale 0.96) on the button: one element for GSAP and one for the CSS.
A click halfway takes every order on from where it is: a label that was coming goes back to its pocket (its place is freed), one that was leaving goes back to its place (if its window was still closing, it opens again there), a row that had not left yet is decided again; if a label’s destination has not changed, it goes on its way. A click on a row during the change waits until the table is still. With the starting values, measured frame by frame: the first new rows can be read between 250 and 333 milliseconds, the table is still within 750 and everything, pockets included, within 900.
Built at the real width of its box, so its words keep their size. The bean temperature is a cubic Hermite curve through the charge (200 degrees), the turning point (92 degrees at 1:15), the yellowing (150), the first crack (196) and the drop, with the slopes taken from the rates of rise around them, which therefore slow down as in a real drum; a point every 4 seconds. At every frame it is drawn again up to q of the way: the line, the area under it, a hairline and the temperature at its tip. The area has a gradient with a stop every twenty-fourth of the time, in the colour of the bean at that moment’s temperature (green, yellow, cinnamon at the first crack, browns), and a mask that lightens it towards the bottom. For an order still in the drum the curve stops at now and the rest of the plan appears dashed.
A table with th scope col and scope row, the café’s name is a button with aria-expanded and aria-controls on the detail row, the pockets are buttons with aria-pressed in a group (the arrows go from one to the next), an aria-live region says Showing 2 packed orders, Esc closes the details. The table keeps the height of all its rows (min-height), so what comes after never jumps. A click from a script (the library’s preview) goes round: Shipped and opens Copper Pot, Packed, All, Roasting and opens Tamarind House, closes and All.
| Name | File | Default | What it changes |
|---|---|---|---|
ORDERS | script.js | [['Little Owl Café', 'Huila, Colombia', 12, 318, 'packed'], …] | The orders: café, coffee, kilos, total in euros and state (roasting, packed or shipped). |
ROASTS | script.js | [['Medium', '8:52', '10:40', 212, 'Red apple, panela', 'Thu 8 Oct'], …] | Each order’s roast: level, first crack, drop (minutes:seconds), drop temperature, what is in the cup, delivery and, for the ones still in the drum, how long they have been in it. |
FLY | script.js | 0.28 | The seconds of a label’s flight into its pocket, or out of it. |
CLOSE | script.js | 0.12 | The seconds a row takes to close in on its label (and a little more to open out of it). |
STAGGER | script.js | 0.025 | The seconds between one order setting off and the next. |
SWING | script.js | 0.45 | How far a label climbs straight up before bending towards its pocket, as a share of the climb: 0 is a straight line. |
DRAW | script.js | 1.3 | The seconds the roast curve takes to draw itself. |
For a wholesaler’s orders, a warehouse’s shipments, a practice’s cases, a help desk’s tickets, the applications to a job: any list split by state, where it helps to see where the rows the filter hides have gone.
At 390 pixels the Coffee and Weight columns go and move under the café’s name (the region and the kilos); the labels narrow to 86% with the width axis of Bricolage Grotesque, the rows are 62 pixels tall and the four pockets share the width, 44 tall, with their words and counts (the icons stay in the labels). The roast goes above its facts. The animations are the same: the labels climb the column of the states up to the pockets.
Nothing flies and nothing slides: the rows that leave disappear, the ones that come fade in in their places, the pill is at once under the pocket chosen and the roast opens already drawn, with a fade.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 17 KB; the libraries: GSAP 28 KB.
Tested on 7 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.