
The layered button
Buttons, Hover, Physics
Free
The Get tickets of a small cinema, in the rich style: in the round end of the red pill stands a popcorn cart with its glass case, its striped canopy and its steel kettle. On a click the kernels pop one after the other, hit the glass and pile up, the kettle tips, the little door opens and the popcorn spills over the pill; then a torn ticket comes out from behind it.
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 popcorn cart 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 popcorn cart button: the Get tickets of a small cinema, with a popcorn cart standing in the round left end
of the pill. Glass case with a gold frame, a striped canopy, a warm lamp and a steel kettle hanging inside.
A click heats the kettle: the kernels pop one after the other, fly against the glass and pile up; the kettle
tips to empty itself, the little door swings open and the popcorn spills onto the pill, one piece bouncing
away. Then a torn ticket rises from behind the pill: Enjoy the show. A second click sweeps it all away -->
<div class="pop" data-pop>
<span class="pop__ticket" aria-hidden="true" data-ticket>
<svg viewBox="0 0 92 46" width="92" height="46">
<defs>
<linearGradient id="tk-paper" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" style="stop-color:var(--ticket)"/>
<stop offset="1" style="stop-color:var(--ticket-deep)"/>
</linearGradient>
</defs>
<path d="M4 3 H80 L84 6 L80 9 L84 12 L80 15 L84 18 L80 21 L84 24 L80 27 L84 30 L80 33 L84 36 L80 39 L84 42 L80 43 H4 a3 3 0 0 1 -3 -3 V6 a3 3 0 0 1 3 -3 Z" fill="url(#tk-paper)"/>
<path d="M80 3 H4 a3 3 0 0 0 -3 3 V40 a3 3 0 0 0 3 3 H80" fill="none" style="stroke:var(--gold)" stroke-opacity="0.55" stroke-width="0.8"/>
<rect x="5" y="7" width="67" height="9" rx="1.5" style="fill:var(--red)"/>
<text x="38.5" y="14" text-anchor="middle" class="pop__ticket-band">ADMIT ONE</text>
<text x="38.5" y="29" text-anchor="middle" class="pop__ticket-film" data-ticket-film>Velvet Hour</text>
<text x="38.5" y="38" text-anchor="middle" class="pop__ticket-seat" data-ticket-seat>Screen 2 · Row F · Seat 7</text>
<path d="M70 7 v32" style="stroke:var(--red)" stroke-opacity="0.35" stroke-dasharray="1.5 2" stroke-width="0.8"/>
</svg>
</span>
<button class="pop__btn" type="button" data-btn data-autoclic>
<span class="pop__fill" aria-hidden="true"></span>
<span class="pop__faces">
<span class="pop__face" data-face="rest">
<span class="pop__word" data-label-text>Get tickets</span>
<span class="pop__time" data-time-text>Fri 9:40</span>
</span>
<span class="pop__face pop__face--busy" data-face="busy" aria-hidden="true">
<span class="pop__shine" data-busy-text>Popping…</span>
</span>
<span class="pop__face pop__face--done" data-face="done" aria-hidden="true">
<span class="pop__done" data-done-text>Enjoy the show</span>
</span>
</span>
</button>
<canvas class="pop__art" aria-hidden="true" data-art></canvas>
<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.
35 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 popcorn cart button” from Animorio and fit it into my siteThe cart, the kettle, the door and the popcorn are drawn on a canvas that covers the component and 40 pixels above it, for the pennant and the flying pieces: spoked wheels, the cabinet with its shading and plate, the warm back of the case with the lamp's cone, the striped canopy cut as a trapezoid with a scalloped hem, the slanting reflections on the glass and the gold frame drawn after the popcorn, so the popcorn sits behind the glass. The pill, the words and the ticket are HTML and SVG under the canvas, which takes no pointer.
Each piece has five to seven lobes around a middle one, with random distances and sizes, drawn three times: the shade moved down, the white, butter on some lobes and light only on the top ones, plus a fleck of hull. When it is born it starts at a quarter of its size, with the orange seed showing, and puffs out in 0.22 seconds with back.out(3.2): the pop.
For the knocks each piece is a circle of 5.2 pixels: GRAVITY, BOUNCE off the walls of the glass and the floor, friction on the floor, and between pieces three passes of separation and a knock between equal masses, so the pile stands up. The frame's step is capped at 1/30 of a second and split in two. When the door is open the right wall below the hinge is gone: the pieces come out, and outside the ground is the edge of the pill, round at its left end. All the chance comes from a generator with a fixed seed, so every show is the same.
A GSAP timeline heats the kettle, opens the lids and calls the pops at shrinking intervals; then it tips the kettle TILT degrees around its rod, pouring the last kernels out with a push to the right, opens the door with back.out and gives the piece nearest the way out a push so it bounces far. At the end the ticket rises from behind the pill with back.out(2.2) and a 7 degree turn. SPEED changes the timeScale of it all.
It is a button: Enter and Space do the same as a click, the keyboard focus has its ring and makes the kettle tremble like the mouse, aria-busy and aria-disabled while it pops, and an aria-live region says Popping your popcorn and then Tickets ready. The three sets of words sit on top of each other and the pill is as wide as the longest, measured once the typeface has arrived.
| Name | File | Default | What it changes |
|---|---|---|---|
LABEL | script.js | Get tickets | The words at rest. |
TIME | script.js | Fri 9:40 | The show time beside them. |
BUSY | script.js | Popping… | The words while the popcorn pops. |
DONE | script.js | Enjoy the show | The words once the ticket is out. |
FILM | script.js | Velvet Hour | The film printed on the ticket. |
SEAT | script.js | Screen 2 · Row F · Seat 7 | The screen, row and seat on the ticket. |
KERNELS | script.js | 34 | How many kernels pop, from 16 to 40. |
GRAVITY | script.js | 1500 | How hard the popcorn falls, in pixels a second squared. |
BOUNCE | script.js | 0.32 | How much the popcorn bounces. |
TILT | script.js | 34 | How far the kettle tips to empty itself, in degrees. |
SPEED | script.js | 1 | How fast the whole show runs. |
For the tickets button of a cinema, a drive-in, an open-air film season or a movie night, or for ordering the snack in a cinema app.
The component is as wide as its words, between 300 pixels and the screen minus 32, and 140 tall with a 60 pixel pill. With no hover, the finger resting on it makes the kettle tremble like the mouse, and the tap does not wait for a double tap.
No flying pops, no trembling and no door swinging open: on a click the pile of popcorn appears already still in the case with a fade, and the ticket appears in its place without rising. The words change by fading.
GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 12 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.