
The dash relief
WebGL, 3D, Hover
Free
The header of a made-up chocolatier, in the rich style: a box of nine pralines built in 3D, petrol green with gold foil stamping and a pink satin ribbon tied in a bow, tilting towards the pointer. A click unties the ribbon and swings the lid open on its hinge; the praline under the pointer, or the finger, lifts out of its paper cup turning, with a puff of cocoa, and its name appears on the card.
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 praline box to open</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<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="shop" data-shop>
<!-- the header of a made-up chocolatier, Maison Noix: a box of nine pralines in 3D that tilts with the pointer.
Click the box: the satin ribbon unties, the lid swings open on its hinge and the pralines show in their paper
cups. The praline under the pointer (or the finger) lifts out of its cup and its name appears on the card -->
<header class="shop__bar">
<span class="shop__brand" data-brand>Maison Noix</span>
<span class="shop__where">Chocolatier since 1931</span>
</header>
<section class="shop__copy">
<p class="shop__kicker">The Saturday box</p>
<h1 class="shop__title">Nine little <em>reasons.</em></h1>
<div class="shop__card" data-card aria-live="polite">
<span class="shop__card-name" data-name>Open the box</span>
<span class="shop__card-note" data-note>Click or tap the lid</span>
</div>
<button class="shop__button" type="button" data-order><span>Order the box</span></button>
</section>
<div class="stage" data-stage tabindex="0" role="button"
aria-label="A box of nine pralines. Click, tap or press Enter to open or close it; move over a praline, or use the arrow keys, to lift it and read its name.">
<div class="stage__shadow" data-shadow></div>
<div class="tilt" data-tilt>
<div class="spin" data-spin>
<!-- on the table, around the box -->
<div class="table-bits" aria-hidden="true">
<div class="crumb crumb--a"></div>
<div class="crumb crumb--b"></div>
<div class="crumb crumb--c"></div>
<div class="nib nib--a"></div>
<div class="nib nib--b"></div>
<div class="menu-card"><span>Nine pieces</span><i>made by hand this morning</i></div>
</div>
<!-- the box: base, walls with an outer and an inner side -->
<div class="box" aria-hidden="true">
<div class="box__floor"></div>
<div class="wall wall--front"><div class="wall__out"><span class="wall__label">Assortiment</span></div><div class="wall__in"></div></div>
<div class="wall wall--back"><div class="wall__out"></div><div class="wall__in"></div></div>
<div class="wall wall--left"><div class="wall__out"></div><div class="wall__in"></div></div>
<div class="wall wall--right"><div class="wall__out"></div><div class="wall__in"></div></div>
<div class="tray-pos">
<div class="tray">
<div class="tissue tissue--l"></div>
<div class="tissue tissue--r"></div>
<div class="tray__grid" data-grid></div>
</div>
</div>
<div class="lid-pos">
<div class="lid" data-lid>
<div class="lid__under"><span>Handmade in small batches</span></div>
<div class="lid__top">
<div class="foil" data-foil>
<svg viewBox="0 0 120 120" class="foil__mark">
<circle cx="60" cy="60" r="54" fill="none" stroke="currentColor" stroke-width="2"/>
<circle cx="60" cy="60" r="48" fill="none" stroke="currentColor" stroke-width="0.8" stroke-dasharray="2 3"/>
<path d="M60 30c-9 0-16 7-16 18 0 13 10 22 16 30 6-8 16-17 16-30 0-11-7-18-16-18z" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M60 34v40M52 46c3 3 13 3 16 0M50 56c4 3 16 3 20 0" fill="none" stroke="currentColor" stroke-width="1.4"/>
</svg>
<span class="foil__name" data-foil-name>Maison Noix</span>
<span class="foil__line">Pralines · Nine pieces</span>
</div>
<div class="ribbon ribbon--h" data-rib-h></div>
<div class="ribbon ribbon--v" data-rib-v></div>
</div>
<div class="skirt skirt--front"><div class="skirt__out"><div class="ribbon-drop"></div></div><div class="skirt__in"></div></div>
<div class="skirt skirt--back"><div class="skirt__out"><div class="ribbon-drop"></div></div><div class="skirt__in"></div></div>
<div class="skirt skirt--left"><div class="skirt__out"><div class="ribbon-drop ribbon-drop--side"></div></div><div class="skirt__in"></div></div>
<div class="skirt skirt--right"><div class="skirt__out"><div class="ribbon-drop ribbon-drop--side"></div></div><div class="skirt__in"></div></div>
<div class="bow" data-bow>
<svg viewBox="0 0 160 110" class="bow__svg">
<defs>
<linearGradient id="satin" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="var(--ribbon-light)"/>
<stop offset="0.45" stop-color="var(--ribbon)"/>
<stop offset="1" stop-color="var(--ribbon-dark)"/>
</linearGradient>
</defs>
<path class="bow__tail" d="M74 60 C66 78 58 92 44 106 L58 104 L64 110 C72 94 78 78 80 62z" fill="url(#satin)"/>
<path class="bow__tail" d="M86 60 C94 78 104 92 118 104 L104 104 L98 110 C90 94 84 78 80 62z" fill="url(#satin)"/>
<path d="M80 52 C60 22 26 14 14 30 C4 44 22 66 50 64 C62 63 72 60 80 56z" fill="url(#satin)"/>
<path d="M80 52 C100 22 134 14 146 30 C156 44 138 66 110 64 C98 63 88 60 80 56z" fill="url(#satin)"/>
<path d="M22 34 C34 26 58 30 74 50" fill="none" stroke="var(--ribbon-dark)" stroke-width="2" opacity="0.5"/>
<path d="M138 34 C126 26 102 30 86 50" fill="none" stroke="var(--ribbon-dark)" stroke-width="2" opacity="0.5"/>
<path d="M28 40 C40 34 54 38 66 50" fill="none" stroke="#fff" stroke-width="2.4" opacity="0.45" stroke-linecap="round"/>
<path d="M132 40 C120 34 106 38 94 50" fill="none" stroke="#fff" stroke-width="2.4" opacity="0.35" stroke-linecap="round"/>
<rect x="70" y="44" width="20" height="20" rx="7" fill="url(#satin)"/>
<path d="M74 48 C78 46 84 46 86 48" fill="none" stroke="#fff" stroke-width="1.6" opacity="0.5" stroke-linecap="round"/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</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.
63 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 praline box to open” from Animorio and fit it into my siteEverything sits in a container with transform-style: preserve-3d, tilted with rotateX by one element and turned with rotateZ by its child (in that order, or the box turns around the screen instead of around itself). The walls are divs rotated 90 degrees around their edge; each has two faces 2 pixels apart, green outside and cream inside, so the right one shows from every side. The sizes are CSS variables worked out on resize, so the box redraws even halfway through an animation.
The lid is a group raised to the height of the walls, with transform-origin on its back edge: opening it is a rotateX up to 112 degrees, with a small overshoot to 116 and a swing back to 108 before it stops. On top is the outer plane with the print and the ribbon, underneath the inner plane turned 180 degrees; both have backface-visibility: hidden, so only the one facing the viewer shows.
Every praline is a stack of six round slices, narrower towards the top, moved with translateZ: tilting the box shows the rounded side. On top is the face with its SVG decoration, drawn with a fixed-seed random generator so it is the same at every visit, and a shine that moves against the pointer.
The spot under the pointer is found with elementFromPoint, which respects 3D transforms. The praline rises with a tween of z and rotationZ with back.out, the shadow in the cup widens and fades, and seven grains of cocoa leave the centre in a circle on the tray’s plane and rise as they fade.
| Name | File | Default | What it changes |
|---|---|---|---|
PRALINES | script.js | nove praline | The nine pralines, in reading order: name, notes on the card and decoration (drizzle, swirl, nuts, hazelnut, gold, bean, dust, petal, orange). |
BRAND | script.js | Maison Noix | The name in the bar and in gold on the lid. |
TILT | script.js | 1 | How much the box tilts towards the pointer: 0 not at all, 2 a lot. |
OPEN_TIME | script.js | 0.9 | The seconds the lid takes to swing open. |
LIFT | script.js | 1 | How high a praline rises out of its cup. |
SPIN | script.js | 1 | How much a lifted praline turns. |
PAUSE | script.js | 1.4 | The seconds between two moves when the box plays by itself. |
--box, --gold, --ribbon, --dark, --milk… | style.css | verde petrolio, oro, rosa | The colours in :root: the box, the gold, the ribbon and the three chocolates of the pralines. |
For the header of a chocolatier, a patisserie, a gift or sweet shop, the page of a gift product or a subscription box. It is a showcase: prices and the buy button also need to be in plain text.
Same box and same animations, adapted sizes: the headline and the card on top, the box below, as large as the space left. A tap opens and closes it, a tap on a praline lifts it, and dragging a finger tilts the box; the stage has touch-action none.
No motion: the box stands still and already open, without the ribbon; the chosen praline stands out because the others fade, and the card changes with a fade.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 23 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.