
The time wheel
Pickers, 3D, Physics
NewFree
The Add to cart button of a small shop, with an open corrugated cardboard box in the round end of the pill, made in real CSS 3D: floor, walls and four flaps, each with its inside and its outside. The flaps ride on real springs and shiver towards the pointer. At a click a candle jar drops into the box, the flaps close one after the other, slapping down and bouncing, a strip of tape runs along the seam and the box hops: Added, Ships tomorrow. Another click cuts the tape, the flaps spring open and another candle goes in: 2 in cart.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React tab holds 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 cardboard box button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the cardboard box button: an Add to cart pill with an open corrugated box in its round right end, built in CSS 3D
with its four flaps up. A click drops a candle jar into the box, the flaps close one after the other on springs
and bounce, a strip of tape runs over the seam and the box hops: Added, Ships tomorrow. Another click cuts the
tape, the flaps spring open and one more candle goes in. The flaps shiver towards the pointer -->
<div class="pack" data-pack>
<button class="pack__btn" type="button" data-btn data-autoclic>
<span class="pack__fill" aria-hidden="true"></span>
<span class="pack__faces">
<span class="pack__face" data-face="rest">
<span class="pack__word" data-wide="Add to cart" data-rest-text>Add to cart</span>
<span class="pack__price" data-price-text>€32</span>
</span>
<span class="pack__face pack__face--busy" data-face="busy" aria-hidden="true">
<span class="pack__shine" data-busy-text>Packing…</span>
</span>
<span class="pack__face pack__face--done" data-face="done" aria-hidden="true">
<span class="pack__lines">
<span class="pack__lead">
<svg class="pack__tick" viewBox="0 0 20 20" aria-hidden="true"><path d="M4.6 10.4l3.5 3.5 7.4-8" pathLength="1" data-tick/></svg>
<span data-lead-text>Added</span>
</span>
<span class="pack__ship" data-ship-text>Ships tomorrow</span>
</span>
</span>
</span>
<!-- the box: a perspective, a layer that hops, and the box turned towards you, every wall and flap a plane with
an outside and an inside -->
<span class="crate" aria-hidden="true" data-crate>
<span class="crate__shadow" data-shadow></span>
<span class="crate__hop" data-hop>
<span class="crate__rig">
<span class="wall wall--floor"><span class="side side--in"></span></span>
<span class="wall wall--back"><span class="side side--out"></span><span class="side side--in"></span></span>
<span class="wall wall--left"><span class="side side--out"></span><span class="side side--in"></span></span>
<span class="wall wall--right"><span class="side side--out"></span><span class="side side--in"></span></span>
<span class="jars" data-jars></span>
<span class="wall wall--front"><span class="side side--out side--print"></span><span class="side side--in"></span></span>
<span class="flap flap--left" data-flap="left"><span class="side side--out"></span><span class="side side--in"></span></span>
<span class="flap flap--right" data-flap="right"><span class="side side--out"></span><span class="side side--in"></span></span>
<span class="flap flap--back" data-flap="back"><span class="side side--out"><span class="tape tape--half" data-tape></span></span><span class="side side--in"></span></span>
<span class="flap flap--front" data-flap="front"><span class="side side--out"><span class="tape tape--half" data-tape></span></span><span class="side side--in"></span></span>
<span class="tab tab--left" data-tab><span class="tape" data-tab-tape></span></span>
<span class="tab tab--right" data-tab><span class="tape" data-tab-tape></span></span>
<span class="cut" data-cut></span>
</span>
</span>
</span>
</button>
<p class="sr" aria-live="polite" data-live></p>
</div>
</main>
<!-- the candle that goes in, copied by the script for each one -->
<template data-jar>
<span class="jar">
<svg class="jar__art" viewBox="0 0 30 40" aria-hidden="true">
<defs>
<linearGradient id="jar-glass" x1="0" x2="1">
<stop offset="0" class="glass-edge"/>
<stop offset="0.18" class="glass"/>
<stop offset="0.32" class="glass-lit"/>
<stop offset="0.5" class="glass"/>
<stop offset="0.85" class="glass-deep"/>
<stop offset="1" class="glass-edge"/>
</linearGradient>
<linearGradient id="jar-wax" x1="0" x2="1">
<stop offset="0" class="wax-deep"/>
<stop offset="0.4" class="wax"/>
<stop offset="1" class="wax-deep"/>
</linearGradient>
</defs>
<path d="M5 6.5h20v27.5q0 3.5-3.5 3.5h-13q-3.5 0-3.5-3.5z" fill="url(#jar-glass)"/>
<ellipse cx="15" cy="9" rx="9.2" ry="2.3" fill="url(#jar-wax)"/>
<path class="jar__wick" d="M15 9V5.6"/>
<rect class="jar__label" x="5" y="17" width="20" height="11"/>
<path class="jar__ink" d="M9 21.2h12M10.5 24.2h9"/>
<path class="jar__gleam" d="M7.4 8.5v25"/>
<ellipse class="jar__lip" cx="15" cy="6.5" rx="10" ry="2.2"/>
</svg>
</span>
</template>
<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.
33 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 cardboard box button” from Animorio and fit it into my siteThe box is a group of spans in transform-style: preserve-3d under a 360 pixel perspective, turned by rotateX(-32deg) rotateY(-34deg). The floor, the four walls and the four flaps are planes put in place with translateZ, translateX and rotateY from the middle of the box; each plane has two faces, the outside and the inside turned half round, with backface-visibility: hidden, so from either side you see the right board. The measures (--bw, --bd, --bh, --fl) live in :root and the script reads them back. The corrugated board is a fine repeating-linear-gradient over the kraft colour; the light of each face is a dark layer with the opacity --shade.
Each flap is hinged on the top edge of its wall (transform-origin: 50% 100%) and turns with rotateX: 0 straight up, 90 shut on the box, negative numbers open outwards. Its angle follows a damped spring worked out every frame, in steps of 1/240 of a second (SWING swings a second, DAMPING); to shut, the target is a little past 90 degrees, so the flap slaps on the stop at 90 and bounces off keeping part of its speed (BOUNCE), then stays pressed down. The long flaps, nearly shut, rise by 0.9 pixels to lie over the short ones. The light on the outside is the dot product of its normal (the wall's, turned up by the angle) with the lamp's direction.
Each flap knows which way it faces on the screen. With the pointer on the button its target rises by 6 degrees plus 22 for how much it faces the pointer, and a shiver of gradient noise on two frequencies (9 and 23 a second) is added, stronger for the flap that faces it; all of it times SHIVER and times how much the pointer is on the button, which GSAP eases up and down. The springs do the rest: the flaps tremble without jerks.
The candle is an SVG (amber glass in a gradient, wax, wick, label) on a plane inside the box's 3D space, turned the opposite way to the box (rotateY(34deg) rotateX(32deg)) so it always faces you; the browser sorts it in depth with the walls, so going in it disappears behind the front face. It falls with an acceleration (power2.in), squashes and comes back on an elastic, and the box gives at the same moment: a scaleY from its floor that comes back with elastic.out. There are two places on the floor: a new candle takes the oldest one's, which lies under the shut flaps and goes unseen.
The tape is made of four pieces: two halves on the long flaps, along their free edge, and two tabs on the ends. The halves stretch together with scaleX after the left tab and before the right one, and read as a single tape. Living on the flaps, when the box opens again the two halves go each with its flap, like a cut tape; first a pale blade runs along the seam. The box hops on a layer of its own (y −12 and back with power2), and squashes from its floor as it lands; the shadow under it narrows while it is in the air.
Three faces on top of each other, as wide as their words: at rest with the invisible wide copy for the widening under the mouse (font-stretch from 86 to 100 percent), Packing… with its sheen, and the final face on two lines. The button is a real button: aria-disabled while it packs, an aria-live region says Packing Fig & cedar candle… and then Added. 1 in cart. Ships tomorrow., and the aria-label says the next click adds another. The first round takes 2.1 seconds and the later ones 2.6, less than the 2.8 between two clicks of the preview.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Add to cart | The words at rest. If they are too long for the room, the type gets smaller. |
PRICE | script.js | €32 | The price, in its small pill next to the words. |
BUSY | script.js | Packing… | The words while the candle goes in and the box shuts. |
ADDED | script.js | Added | The small line the first time, after the tick. |
UNIT | script.js | in cart | The small line from the second time, after the number. |
SHIP | script.js | Ships tomorrow | The big line once the box is shut: in a real site your shop says the date. |
PRODUCT | script.js | Fig & cedar candle | The product, for the aria-live region: in a real site it is the one on the page, and the adding has to be wired to your cart. |
SWING | script.js | 3.4 | The flaps' swings a second on their springs: higher, stiff board and quick snaps; lower, soft slow flaps. |
DAMPING | script.js | 0.26 | How hard the flaps are held back: 0.08 they flap and rock a long time, 1 they go straight to their place. |
BOUNCE | script.js | 0.38 | How much speed a flap keeps when it slaps on the box: 0 it stops dead, 0.8 it bounces several times. |
SHIVER | script.js | 1 | How much the flaps shiver and rise towards the pointer: 0 they stay still. |
OPEN | script.js | [-72, -34, -44, -66] | The open flaps' angles, in degrees past upright: front, back, left, right. Past −60 a flap hangs outwards and shows its inside. |
For the Add to cart button of a shop that ships: candles, ceramics, tea, cosmetics, books, handmade things. The box shutting tells the shipping, and the Ships tomorrow line is the right place for the real delivery date. It has to be wired to your cart and your shop's date: here it is all simulated.
The pill is about 260 pixels wide: at 390 it fits with room to spare, and if the words do not fit the type gets smaller. A finger on the button makes the flaps shiver towards it and the tap adds; the box keeps its size.
No springs and no hops: the flaps stay still and open, they do not shiver, the fill does not swell and the words do not widen (under the mouse the fill only lightens a little). At a click the box's faces fade out and come back already shut, taped, with the candle inside, while Packing… and then Added, Ships tomorrow fade in. Another click does the same and the number goes up.
GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 12 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.