Skip to content
animorio
IT
All components

The grocer's scale button

Buttons, Physics, SVGGSAP, CSS onlyAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The Add to cart of a grocer's that sells by weight, with a spring scale in the round end of the pill: the enamel dial with its ticks and the red needle behind a chrome rim, and over the pill the brass pan on its column. At a click a bag of coffee drops on the pan: the pan sinks, the spring really swings (a damped oscillator worked out every frame) and the needle, geared to the pan, settles on the weight, while the pill reads the needle and works the price out from the weight, like a computing scale. Then Added · 1 in cart and 250 g · €6.40. The next click drops a second bag on top of the first: the pan sinks further, weight and bill go up.

The code

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 grocer's scale 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 grocer's scale button: the Add to cart of a grocer's shop, with a spring scale in the round left end of
         the pill. Its dial is where an icon would be, and its brass pan stands just over the pill. A click drops a bag
         of coffee on the pan: the pan sinks, the spring swings (a real damped oscillator), the needle settles on the
         weight and the price is worked out from it. The next click adds a second bag on top of the first -->
    <div class="scale" data-scale>
      <div class="scale__unit">
        <button class="scale__btn" type="button" data-btn data-autoclic>
          <span class="scale__fill" aria-hidden="true"></span>
          <span class="scale__faces">
            <span class="scale__face" data-face="rest">
              <span class="scale__word" data-wide="Add to cart" data-rest-text>Add to cart</span>
              <span class="scale__price" data-rest-price>€6.40</span>
            </span>
            <span class="scale__face scale__face--two" data-face="busy" aria-hidden="true">
              <span class="scale__small" data-busy-text>Weighing</span>
              <span class="scale__big scale__digits" data-reading>0 g · €0.00</span>
            </span>
            <span class="scale__face scale__face--two" data-face="done" aria-hidden="true">
              <span class="scale__big" data-done-text>Added · 1 in cart</span>
              <span class="scale__small scale__digits" data-done-sub>250 g · €6.40</span>
            </span>
          </span>
        </button>
        <!-- the scale: dial, column, pan and bags, drawn by script.js; the pointer goes through it to the button -->
        <svg class="scale__art" viewBox="-20 -92 104 152" aria-hidden="true" data-art></svg>
      </div>
      <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>

Playground

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.

30 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The grocer's scale button” from Animorio and fit it into my site

What it does

On a white background a shop-green pill 60 pixels tall, with a thread of light along the top and a soft shadow under it; on the right Add to cart in cream Bricolage Grotesque and the price €6.40 in a pale little pill. In the round left end, where the icon would be, the dial of a kitchen scale 47 pixels across: the chrome rim with its shadow on the pill, the ivory enamel face with ticks every 25 grams, longer every 100 and every 250, the figures 250, 500, 750 and 1kg in red, the slim red needle with its counterweight and the chrome cap on the pivot, and over it the glint of the glass. From the top of the dial a chrome column rises out of the pill and holds up the brass pan, a shallow dish seen a little from above. Passing over the pill the words widen, the fill swells with a bounce and the pan trembles a little, as if brushed: the needle gives a small start. At a click a bag of coffee (kraft paper, its top folded over with the metal tie, a cream label with a bean) drops from above, appearing as it comes, and its shadow darkens on the pan; it lands, squashes and straightens. The pan sinks under the weight, comes back up, sinks again less and less, and the needle swings with it around 250; meanwhile the pill says Weighing and under it the grams and the price the needle reads at that moment, running back and forth. When the swing is too small to read, Added · 1 in cart arrives with 250 g · €6.40 under it. The next click drops a second bag on top of the first: the ones under it squash a little, the pan sinks further, the needle passes 500 and comes back, and the pill says Added · 2 in cart, 500 g · €12.80. With the third bag the scale is full; the click after lifts the bags off one after the other, the pan springs back up and the needle knocks against the zero stop, and the button is back to Add to cart. With the keyboard the focus shows a green ring and the pan trembles; Enter or Space add. On a phone a tap makes the pan tremble and adds.

How it works

The scale in an SVG

A single SVG lies over the pill and stands out above it (pointer-events: none, so the click reaches the button): its coordinates are the pill's, with the viewBox starting 92 pixels higher. The still parts (rim, face, ticks, figures, collar, glass) are written once; the gradients take their colours with stop-color: var(--chrome) and the like, so the Playground's swatches change them without redrawing. Every frame only the transform attributes of the needle, its shadow, the pan and the bags change, and the height of the column, which shortens as the pan sinks into the collar.

The spring and the pan

The pan is a mass on a spring with a damper: M · d'' = K · (PXG · load − d) − c · d', with M = the pan's 170 grams plus the bags. K comes from SWING (the swings a second with one bag), and PXG = 0.009 pixels a gram says where the pan stops for each weight. The damper is c = 2 · σ · M with σ = DAMPING · 2π · SWING: with more bags the pan swings slower, as on a real scale, but calms down in the same time. Steps of 1/240 of a second, the frame capped at 1/30. Over the zero there is a stop: a pan springing back up knocks on it and bounces with 35% of its speed.

The falling bag

The bag falls with gravity (2600 pixels a second a second) from DROP pixels over its place, which is the pan or the bag under it, read at every step because the pan moves. When it touches, its weight goes onto the spring all at once and the momentum is shared in an inelastic knock: the pan gets 0.06 · m · v / M. The sudden step of the load alone would carry the needle to nearly twice the weight; the knock adds the rest. The bag has two springs of its own, its squash (scaleY down, scaleX up) and its tilt, and the bags under it take a share of the squash.

The needle and the price

The needle is geared to the pan as by a rack: angle = (d / PXG) · 0.3 degrees, so a kilo is 300 degrees clockwise from the top. While it weighs, the pill reads the needle every frame: grams = d / PXG rounded, price = grams · PRICE / BAG, written only when they change, in tabular figures so the line does not jitter. The size of the swing is estimated from its energy, √((d − d0)² + (d' / ω)²): under 6 grams the needle counts as still and Added arrives with the exact weight.

The states and the cycle

Three faces on top of each other, as wide as their words: at rest, while it weighs, and added; the pill is measured once on the widest (the heaviest reading, the most bags), so it never changes width. The button is a real button with aria-disabled while the bag falls and the needle swings, an aria-live region that says Weighing… and then Added. 1 in cart, 250 g, €6.40, and an aria-label that says what the next click does (Add another 250 g, or Clear the scale when it is full). Falling and settling take about 1.7 seconds, less than the 2.8 between two clicks of the preview: each click adds a bag, and the fourth empties the scale.

Values to change

NameFileDefaultWhat it changes
LABELscript.jsAdd to cartThe words at rest, before the price.
PRODUCTscript.jsCoffee beansWhat the bags hold: the screen reader says it in the button's label.
WEIGHINGscript.jsWeighingThe small line while the needle swings; the aria-live region says it too.
ADDEDscript.jsAddedThe word once the needle settles, before the count in the cart.
BAGscript.js250The grams in one bag: they say where the pan stops and where the needle points. The dial reads up to a kilo, so the scale holds at most the bags that fit.
PRICEscript.js6.4The price of one bag in euros: the price a gram the pill works with while the needle swings comes from it.
MOSTscript.js3How many bags the pan holds: the click after the last one lifts them off and goes back to rest.
SWINGscript.js2.2The spring's swings a second with one bag: higher, a stiff quick spring; lower, a soft slow one.
DAMPINGscript.js0.2How hard the damper holds the pan back: 0.05 it swings a long time, 0.2 it passes the weight a couple of times and stops, 1 it reaches the weight without passing it.
DROPscript.js64How high the bag falls from, in pixels: the higher, the harder it hits the pan and the further the needle goes past the weight.
BRUSHscript.js1How hard a passing pointer brushes the pan: 0 it stays still, 3 the needle jumps by some seventy grams.

Try them in the Playground

When to use it

For the Add to cart of anyone selling by weight: grocers, coffee roasters, tea, spices, dried fruit, pulses, cheese, online groceries. It also works where things are sold by measure or by quantity (fabric by the metre, yarn, paper) by changing the bag and the figures. The button fakes the cart: in a real site the click calls your cart, and the bag drops when the server answers.

On phones

The pill is about 260 pixels wide and the pan stands some fifty over it: at 390 pixels it fits with room to spare. A tap makes the pan tremble, squashes the fill and adds a bag. The scale keeps its size.

With reduced motion

The bag does not fall: it fades in on its place, and the pan and the needle are on the weight already, without swinging. The words fade in, the pan does not tremble under a passing pointer, the pill does not swell and the words do not widen (under the mouse the fill only lightens a little). With the scale full the click fades the bags away.

Pitfalls

  • With the damping proportional to √(K · M), the one an oscillator with a fixed damping ratio has, three bags took nearly three seconds to calm down and the preview's next click fell while the scale was still weighing, so it was lost. Now the damper is c = 2 · σ · M: the frequency drops with the load as on a real scale, but the time to calm down stays at about 1.4 seconds.
  • A damped oscillator's needle never quite stops: waiting until it was still to within a gram, Added came late. Now the size of the swing is estimated from its energy, and under 6 grams the pill says Added with the exact weight while the needle finishes settling on its own; the loop stops drawing only when everything is truly still.
  • The bag drawn as a pillow with a crimped seal at each end looked like a sweet. Now it has its top folded over with the metal tie at one end and the bottom gusset at the other, like coffee bags do.
  • The group of the dial's figures had two style attributes (one for the colour, one for the type's width): the parser keeps only the first, and the figures came out wide. Now colour and font-stretch live in the same style.

Libraries

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.

Testing

Tested on 12 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

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.

All components