
The layered button
Buttons, Hover, Physics
Free
The Add to bag button of a clothes shop: over a coffee-coloured pill, on a steel rail, a pale blue linen shirt hangs on a beech hanger, and in the end of the pill stands a kraft paper bag. The shirt is truly simulated cloth, point by point, with folds that catch the light: the pointer blows on it and it sways. At a click the hanger slides along the rail to the bag, swings with its inertia and the shirt swings after it; on the way it folds as in a shop, sleeves behind and tail up, the hanger dips and lets it drop into the bag, which gives and springs back: Added · Size M, with Undo. Another click takes it out, unfolds it and hangs it back.
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 hanger button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the hanger button: the Add to bag of a clothes shop. Over the pill, on a short steel rail, a small linen shirt
hangs on a wooden hanger; the shirt is simulated cloth (a grid of points held by links) drawn on a canvas with
the light on its folds, and a paper bag stands in the round end of the pill. The pointer blows on the cloth;
a click slides the hanger along the rail, folds the shirt and drops it in the bag; another click hangs it up
again -->
<div class="hang" data-hang>
<canvas class="hang__cloth" aria-hidden="true" data-cloth></canvas>
<button class="hang__btn" type="button" data-btn data-autoclic>
<span class="hang__fill" aria-hidden="true"></span>
<span class="hang__faces">
<span class="hang__face" data-face="rest">
<span class="hang__word" data-wide="Add to bag · €89" data-rest-text>Add to bag · €89</span>
</span>
<span class="hang__face hang__face--busy" data-face="busy" aria-hidden="true">
<span class="hang__shine" data-busy-text>Adding…</span>
</span>
<span class="hang__face hang__face--done" data-face="done" aria-hidden="true">
<span class="hang__done">
<svg class="hang__tick" viewBox="0 0 16 16" aria-hidden="true" data-tick><path d="M3.2 8.6l3 3 6.6-7"/></svg>
<span data-done-text>Added · Size M</span>
</span>
<span class="hang__undo" data-undo-text>Undo</span>
</span>
</span>
</button>
<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>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.
24 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 hanger button” from Animorio and fit it into my siteThe flat shirt is an outline (collar, sloping shoulders, short sleeves, sides, rounded tail) and a grid of 20 by 17 points 2.8 pixels apart: only the cells that touch the outline are kept, about two hundred, with their points. Between the points there are links along the rows, along the columns and across both diagonals of each cell (so the cloth neither stretches nor shears) and a softer pair two points apart, which resists sharp bends. The rest length of every link is measured on the shirt already draped: a few soft folds in depth from the chest down, so hanging still it is never a flat sheet.
Each point has its position now and the one before: its speed is the difference, held back a little by the air, and the forces (gravity, the blowing, the springs of the folding) are added times the square of the step. Then the links are brought back to their length twenty times a step, in fixed steps of 1/120 of a second (the frame capped at 1/30). The points of the shoulders are pinned to the hanger and follow it; the limits are constraints too: the cloth never comes down onto the words of the pill, and in the bag it stays between the walls and above the floor.
The shirt is painted once on a canvas off screen, four times finer: the linen with its weave of threads, the seams, the pocket, the placket and its buttons, the collar with its shadow; and a second time plain linen, for the wrong side. Every frame the normal of each point (the cross product of its neighbours) gives a shade map and a sheen map as large as the grid: they are laid on the painting, enlarged with smoothing, in multiply and in screen. Then every cell is drawn in two triangles, each with its piece of the painting carried by the affine transform from the three flat points to the three points now, clipped a little wider so no seams show; the cells are sorted by depth and a triangle turned over shows the wrong side.
The hanger slides along the rail with a GSAP tween; its acceleration is read every frame (finite differences, filtered) and drives the hanger's angle like a pendulum's hung from the hook: θ'' = −ω0² sin θ − 2ζω0 θ' − SWAY · a / L · cos θ. Starting it lags, stopping it runs on, and the cloth hung by its shoulders follows on its own, lagging more down at the tail.
Folding is a spring for each point towards its place in the folded shirt, worked out from its flat place: beyond two upright lines the sleeves and the sides turn behind (up to 0.94 of half a turn), then below a level line the tail comes up behind. The sharp bends resist less while it folds, and meanwhile the outer shoulders leave the hanger. When the hanger dips, the parcel passes to a frame that falls on its own with gravity and hits the floor of the bag with a bounce; the springs stay, softer, and the links and the walls crumple it a little. The bag squashes from its floor and comes back with elastic.out.
Three faces on top of each other, as wide as their words: Add to bag · €89 (with an invisible wide copy holding the room of the words that widen), Adding… with a sheen running through it, and Added · Size M with the tick drawing itself through its dash and the small Undo. The button is a real button: aria-disabled while it works, an aria-live region says Adding…, Added · Size M. In your bag. and Removed from your bag., and the aria-label says what the next click will do. The whole cycle fits in the 2.8 seconds between two clicks of the preview.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Add to bag · €89 | The words at rest, with the price. |
BUSY | script.js | Adding… | The words while the shirt goes into the bag; the aria-live region says it too. |
DONE | script.js | Added · Size M | The words with the shirt in the bag. In a real shop the size chosen goes here. |
UNDO | script.js | Undo | The small pill on the right: another click hangs the shirt back. |
SLIDE | script.js | 1 | The seconds the hanger takes to slide along the rail: shorter, more jolt and more swing. |
SWAY | script.js | 1 | How far the hanger swings on the start and the stop: 0 it stays upright (the cloth still sways). |
BLOW | script.js | 1 | How hard the pointer blows on the shirt: 0 not at all, 2 a gust. |
DRAPE | script.js | 1 | How deep the soft folds of the shirt are as it hangs still: 0 a smooth sheet. |
For the button that puts a garment in the cart: a clothes shop, for linen and cotton, made-to-measure shirts, workwear or children's clothes, a boutique or a second-hand shop. The hanger suits pages with one product and the size already chosen. It has to be wired to your real cart: here adding is simulated.
The pill is 296 pixels wide and the component 130 tall: at 390 pixels it fits with room to spare. A finger resting near the shirt blows like the pointer, and a tap on the pill runs the whole round: slide, fold, bag. The sizes stay the computer's.
The shirt hangs still on the hanger with its drape, the pointer does not blow, the hanger neither slides nor swings, the pill does not swell and the words do not widen (under the mouse the fill only lightens a little). At a click the shirt fades away leaving the hanger empty and Added · Size M fades in; another click brings it back the same way.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 18 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.