
The cookie banner
Consent, Transitions, SVG, Physics
NewFree
A florist's cart where the order is the bouquet: every stem in the cart is a stem in the drawing, on its own spring. With plus a stem comes in from above and the others open up to make room, swinging; with minus one is pulled out; a line removed pulls out all its stems (with Undo, which puts each one back in its place) and, once delivery is free, kraft paper wraps the bouquet and a ribbon is tied into a bow.
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 florist's cart</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/young-serif@5.3.0/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/wix-madefor-text@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the drawings: flower heads, leaves; the bouquet and the thumbnails use them -->
<svg class="defs" aria-hidden="true" focusable="false">
<defs>
<radialGradient id="g-ranunculus" cx="0.45" cy="0.35" r="0.75">
<stop offset="0" class="st-ra-hi"/>
<stop offset="1" class="st-ra"/>
</radialGradient>
<linearGradient id="g-tulip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" class="st-tu-hi"/>
<stop offset="1" class="st-tu"/>
</linearGradient>
<radialGradient id="g-anemone" cx="0.5" cy="0.5" r="0.6">
<stop offset="0.35" class="st-an-in"/>
<stop offset="1" class="st-an"/>
</radialGradient>
<g id="h-ranunculus">
<circle r="16" class="ra-out"/>
<path d="M-15.6 2.4A16 16 0 0 0 15.6 2.4A17 13 0 0 1-15.6 2.4Z" class="ra-shade"/>
<circle cy="-1.6" r="12.4" class="ra-mid"/>
<path d="M-12 0.6A12.4 12.4 0 0 0 12 0.6A13 9.6 0 0 1-12 0.6Z" class="ra-shade"/>
<circle cy="-3" r="8.8" class="ra-out"/>
<path d="M-8.4 -1.6A8.8 8.8 0 0 0 8.4 -1.6A9.2 6.8 0 0 1-8.4 -1.6Z" class="ra-shade"/>
<circle cy="-4.2" r="5.2" class="ra-mid"/>
<circle cy="-4.8" r="2.3" class="ra-heart"/>
</g>
<g id="h-tulip">
<path d="M-11.5 -3C-12.5 -13 -7 -19.5 0 -21.5C7 -19.5 12.5 -13 11.5 -3C10.5 6.5 5.5 11 0 11C-5.5 11 -10.5 6.5 -11.5 -3Z" class="tu-back"/>
<path d="M-12.5 -6C-13.5 -15.5 -9.5 -21 -4.5 -23C-2 -15 2.5 -6 3 2.5C3 8.5 1 11.2 0 11.2C-6.5 11.2 -11.5 5 -12.5 -6Z" class="tu-side"/>
<path d="M12.5 -6C13.5 -15.5 9.5 -21 5 -22.5C2 -15 -1.5 -6 -2 2.5C-2 8.5 0 11.2 1 11.2C6.5 11.2 11.5 5 12.5 -6Z" class="tu-side"/>
<path d="M-6.5 -1.5C-7.2 -12 -3.2 -18.5 0 -20.8C3.2 -18.5 7.2 -12 6.5 -1.5C5.8 6 3.2 10.4 0 10.8C-3.2 10.4 -5.8 6 -6.5 -1.5Z" class="tu-front"/>
<path d="M-2.6 -14C-3.4 -8 -3 -1 -1.6 5" class="tu-gleam"/>
</g>
<g id="h-anemone">
<g transform="scale(1 0.88)">
<path d="M0 -1C-7 -4 -9.5 -12 -6 -16.5C-3 -19.5 3 -19.5 6 -16.5C9.5 -12 7 -4 0 -1Z" class="an-petal" transform="rotate(10)"/>
<path d="M0 -1C-7 -4 -9.5 -12 -6 -16.5C-3 -19.5 3 -19.5 6 -16.5C9.5 -12 7 -4 0 -1Z" class="an-petal" transform="rotate(130)"/>
<path d="M0 -1C-7 -4 -9.5 -12 -6 -16.5C-3 -19.5 3 -19.5 6 -16.5C9.5 -12 7 -4 0 -1Z" class="an-petal" transform="rotate(250)"/>
<path d="M0 -1C-7.5 -4 -10 -12.5 -6.4 -17.4C-3.2 -20.4 3.2 -20.4 6.4 -17.4C10 -12.5 7.5 -4 0 -1Z" class="an-petal" transform="rotate(70)"/>
<path d="M0 -1C-7.5 -4 -10 -12.5 -6.4 -17.4C-3.2 -20.4 3.2 -20.4 6.4 -17.4C10 -12.5 7.5 -4 0 -1Z" class="an-petal" transform="rotate(190)"/>
<path d="M0 -1C-7.5 -4 -10 -12.5 -6.4 -17.4C-3.2 -20.4 3.2 -20.4 6.4 -17.4C10 -12.5 7.5 -4 0 -1Z" class="an-petal" transform="rotate(310)"/>
<circle r="6.6" class="an-ring"/>
<circle r="3.9" class="an-heart"/>
<circle cx="-1.2" cy="-1.3" r="1.3" class="an-gleam"/>
</g>
</g>
<g id="lf-eucalyptus">
<ellipse rx="6" ry="5.2" class="eu-leaf"/>
<path d="M-3.8 -1.2C-1.6 -3 1.8 -3 3.6 -1.4" class="eu-bloom"/>
</g>
<path id="lf-tulip" d="M0 0C5.5 -9 7.5 -27 2.6 -44C-2.6 -28 -4.4 -11 0 0Z" class="tu-leaf"/>
</defs>
</svg>
<main class="stage">
<!-- a florist's cart, and the order is the bouquet: every stem in it is a stem in the drawing, on its own spring.
More stems open the bouquet, a line removed is pulled out of it (with undo), and from the free delivery
total the bouquet is wrapped in kraft paper and tied with a ribbon -->
<section class="cart" aria-labelledby="cart-title" data-cart data-autoclic>
<header class="cart__head">
<h2 class="cart__title" id="cart-title">Your bouquet</h2>
<p class="cart__count" data-count></p>
</header>
<div class="table" data-table>
<svg class="bouquet" viewBox="0 0 360 178" aria-hidden="true" data-bouquet>
<g data-paper-back>
<path class="paper-back" d="M166 170L86 52C96 40 108 36 120 42C130 26 148 24 160 34C172 18 190 18 200 32C212 22 230 26 238 42C250 36 264 40 274 52L194 170Z"/>
<path class="paper-back-fold" d="M120 42C126 60 132 80 142 104M200 32C198 56 194 82 188 106M238 42C232 62 224 84 214 106"/>
</g>
<g data-stems-back></g>
<g data-stems-front></g>
<g class="twine-wrap">
<g class="twine" data-twine>
<path d="M171 134C176 137 184 137 189 134M170.5 138C176 141 184 141 189.5 138"/>
<path d="M189 136C194 139 196 145 194 150"/>
</g>
</g>
<g data-paper-left>
<path class="paper-front" d="M186 172L92 78C112 86 140 92 168 90L192 150Z"/>
<path class="paper-lip" d="M92 78C112 86 140 92 168 90L166 97C140 98 114 92 92 78Z"/>
</g>
<g data-paper-right>
<path class="paper-front paper-front--r" d="M174 172L268 78C248 86 220 92 192 90L168 150Z"/>
<path class="paper-lip" d="M268 78C248 86 220 92 192 90L194 97C220 98 246 92 268 78Z"/>
</g>
<g data-ribbon>
<path class="ribbon" d="M152 130C170 135 190 135 208 130L207 138C190 143 170 143 153 138Z" data-band/>
<g data-tails>
<path class="ribbon ribbon--dark" d="M178 136C174 146 168 156 160 166L166 166L168 172C176 160 181 148 182 137Z" data-tail-l/>
<path class="ribbon" d="M182 136C186 146 192 156 200 166L194 166L192 172C184 160 179 148 178 137Z" data-tail-r/>
</g>
<g data-loops>
<path class="ribbon" d="M178 133C170 122 154 120 152 128C150 136 166 140 178 136Z"/>
<path class="ribbon" d="M182 133C190 122 206 120 208 128C210 136 194 140 182 136Z"/>
<path class="ribbon-fold" d="M176 132C168 126 160 125 157 128M184 132C192 126 200 125 203 128"/>
<rect class="ribbon ribbon--knot" x="175.5" y="129.5" width="9" height="8.5" rx="3"/>
</g>
</g>
</svg>
<p class="goal" data-goal></p>
<button class="restart" type="button" hidden data-restart>Start with the house mix</button>
</div>
<ul class="lines" aria-label="Stems in your bouquet" data-lines></ul>
<div class="sums">
<p class="sums__row"><span>Subtotal</span><span class="sums__n" data-sub></span></p>
<p class="sums__row"><span>Delivery</span><span class="sums__n"><s class="sums__was" data-was></s><span data-ship></span></span></p>
</div>
<button class="pay" type="button" data-pay>
<span class="pay__face" data-pay-face>
<svg class="pay__spin" viewBox="0 0 20 20" aria-hidden="true" data-pay-spin><path d="M10 2.6a7.4 7.4 0 1 1-7.4 7.4"/></svg>
<svg class="pay__tick" viewBox="0 0 20 20" aria-hidden="true" data-pay-tick><path d="M4.6 10.4l3.6 3.6 7.2-7.6"/></svg>
<span class="pay__label" data-pay-label></span>
</span>
</button>
<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.
48 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 florist's cart” from Animorio and fit it into my siteEvery stem has an angle and a length that chase their place with a spring (SPRING: stiffness and damping), worked out in two half steps a frame with the step held to 1/30 of a second. A new stem starts 72 units higher and comes down into its place; a stem pulled out aims 96 units further up and fades meanwhile. A single requestAnimationFrame moves every stem and stops when none is moving any more: at rest nothing is drawn.
Every stem is born with a preference between left and right taken in golden-ratio steps, so the kinds stay mixed (the eucalyptus goes towards the edges, like the greenery round the flowers). The stems in order of preference share the fan, up to SPREAD degrees wide and wider the more there are; the middle ones are longer, so the heads make a dome, and one row stands in front of the other. Wrapped, the fan closes by 13%.
Every frame the stem is an SVG path: straight under the binding, then a quadratic curve up to the head. The control point sits at a tighter angle, so the stem rises like a fountain and opens out towards the head, and it moves with the turning speed: the middle of the stem goes ahead and the head trails, as on a real flower. Pushed up, the part under the binding gets shorter. The head is a use turned along the curve's tangent, and the eucalyptus leaves are placed along the curve with the Bézier's points and tangents.
Remove pulls the stems out one after the other and keeps their properties (preference, row, length, tilt): Undo makes them again, the same and in the same drawing order, so they go back to their place and their depth. The line changes height measured before and after swapping the body for the strip; Undo's ring is a circle whose stroke-dashoffset runs for UNDO seconds together with a GSAP delayedCall, and both stop with the pointer or the focus on the strip.
Free delivery starts a timeline on the paper's SVG groups, with svgOrigin at the cone's point: the sheet behind grows from the point, the two in front turn and stretch with back.out, the ribbon spreads, a push down on all the springs sinks the bouquet into the paper, and the bow and the tails arrive with elastic.out. Going back under, the sheets fold down into the point instead of opening sideways, so they never go over the line beside them. A click from a script (the library's preview) is a pretend customer: one more ranunculus, the tulips removed, Undo, one more eucalyptus and one less, one ranunculus less.
| Name | File | Default | What it changes |
|---|---|---|---|
ITEMS | script.js | [['Ranunculus', 3.2, 4, 'ranunculus'], …] | The cart's lines: name, price of a stem in euros, how many and drawing (ranunculus, tulip, anemone or eucalyptus). |
FREE | script.js | 35 | The subtotal from which delivery is free and the bouquet is wrapped, in euros. |
DELIVERY | script.js | 4.95 | What delivery costs below that figure, in euros. |
MAX | script.js | 9 | The most stems of one kind. |
SPRING | script.js | [230, 0.42] | The stems' springs: how stiff they are and how much they are held back (1 = no swing). |
SPREAD | script.js | 62 | How far the bouquet opens at most, in degrees either side of upright. |
WRAP | script.js | 0.7 | The seconds the paper takes to wrap the bouquet. |
UNDO | script.js | 5 | The seconds Undo stays after a line is removed. |
BRUSH | script.js | 1 | How much the flowers sway when the pointer brushes through them (0 = not at all). |
For the cart of a florist, a plant nursery or anyone who puts something together piece by piece (a gift hamper, a box of sweets, a crate of fruit): a list with a total, a threshold to reach and a live preview of what is being bought.
At 390 pixels the cart is 358 wide and the bouquet is redrawn at the same scale; every button takes a finger and does not wait for a double tap (touch-action manipulation). A finger moving over the bouquet sways it like the mouse, and the page still scrolls vertically (touch-action pan-y). Stems lifted and others see-through only with the mouse or the keyboard, never after a tap.
No springs and no movement: stems appear and disappear with a fade straight in their place, the paper and the bow come and go with a fade, figures change without blurring, lines change without animating their height and the flowers do not sway under the pointer.
GSAP 3.15.0, Young Serif 5.3.0, Wix Madefor Text (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 16 KB; the libraries: GSAP 28 KB.
Tested on 6 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.