Vai al contenuto
animorio
EN
Tutti i componenti

Il pulsante della scatola di cartone

Pulsanti, 3D, FisicaSolo CSS, GSAPImpegnativaPeso compresso: Demo 13 KBGSAP 28 KBGratisScarica lo zip

Il pulsante Add to cart di una piccola bottega, con una scatola di cartone ondulato aperta nella punta tonda della pillola, fatta in vero CSS 3D: fondo, pareti e quattro alette, ognuna col suo dentro e il suo fuori. Le alette stanno su molle vere e fremono verso il puntatore. Al clic un vasetto di candela cade nella scatola, le alette si chiudono una dopo l'altra sbattendo e rimbalzando, un nastro adesivo corre sulla fessura e la scatola fa un saltello: Added, Ships tomorrow. Un altro clic taglia il nastro, le alette si riaprono di scatto e entra un'altra candela: 2 in cart.

Il codice

Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nella scheda React la stessa demo è già un componente, che spegne le sue animazioni quando esce dalla pagina.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The cardboard box 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 cardboard box button: an Add to cart pill with an open corrugated box in its round right end, built in CSS 3D
         with its four flaps up. A click drops a candle jar into the box, the flaps close one after the other on springs
         and bounce, a strip of tape runs over the seam and the box hops: Added, Ships tomorrow. Another click cuts the
         tape, the flaps spring open and one more candle goes in. The flaps shiver towards the pointer -->
    <div class="pack" data-pack>
      <button class="pack__btn" type="button" data-btn data-autoclic>
        <span class="pack__fill" aria-hidden="true"></span>
        <span class="pack__faces">
          <span class="pack__face" data-face="rest">
            <span class="pack__word" data-wide="Add to cart" data-rest-text>Add to cart</span>
            <span class="pack__price" data-price-text>€32</span>
          </span>
          <span class="pack__face pack__face--busy" data-face="busy" aria-hidden="true">
            <span class="pack__shine" data-busy-text>Packing…</span>
          </span>
          <span class="pack__face pack__face--done" data-face="done" aria-hidden="true">
            <span class="pack__lines">
              <span class="pack__lead">
                <svg class="pack__tick" viewBox="0 0 20 20" aria-hidden="true"><path d="M4.6 10.4l3.5 3.5 7.4-8" pathLength="1" data-tick/></svg>
                <span data-lead-text>Added</span>
              </span>
              <span class="pack__ship" data-ship-text>Ships tomorrow</span>
            </span>
          </span>
        </span>

        <!-- the box: a perspective, a layer that hops, and the box turned towards you, every wall and flap a plane with
             an outside and an inside -->
        <span class="crate" aria-hidden="true" data-crate>
          <span class="crate__shadow" data-shadow></span>
          <span class="crate__hop" data-hop>
            <span class="crate__rig">
              <span class="wall wall--floor"><span class="side side--in"></span></span>
              <span class="wall wall--back"><span class="side side--out"></span><span class="side side--in"></span></span>
              <span class="wall wall--left"><span class="side side--out"></span><span class="side side--in"></span></span>
              <span class="wall wall--right"><span class="side side--out"></span><span class="side side--in"></span></span>
              <span class="jars" data-jars></span>
              <span class="wall wall--front"><span class="side side--out side--print"></span><span class="side side--in"></span></span>
              <span class="flap flap--left" data-flap="left"><span class="side side--out"></span><span class="side side--in"></span></span>
              <span class="flap flap--right" data-flap="right"><span class="side side--out"></span><span class="side side--in"></span></span>
              <span class="flap flap--back" data-flap="back"><span class="side side--out"><span class="tape tape--half" data-tape></span></span><span class="side side--in"></span></span>
              <span class="flap flap--front" data-flap="front"><span class="side side--out"><span class="tape tape--half" data-tape></span></span><span class="side side--in"></span></span>
              <span class="tab tab--left" data-tab><span class="tape" data-tab-tape></span></span>
              <span class="tab tab--right" data-tab><span class="tape" data-tab-tape></span></span>
              <span class="cut" data-cut></span>
            </span>
          </span>
        </span>
      </button>
      <p class="sr" aria-live="polite" data-live></p>
    </div>
  </main>

  <!-- the candle that goes in, copied by the script for each one -->
  <template data-jar>
    <span class="jar">
      <svg class="jar__art" viewBox="0 0 30 40" aria-hidden="true">
        <defs>
          <linearGradient id="jar-glass" x1="0" x2="1">
            <stop offset="0" class="glass-edge"/>
            <stop offset="0.18" class="glass"/>
            <stop offset="0.32" class="glass-lit"/>
            <stop offset="0.5" class="glass"/>
            <stop offset="0.85" class="glass-deep"/>
            <stop offset="1" class="glass-edge"/>
          </linearGradient>
          <linearGradient id="jar-wax" x1="0" x2="1">
            <stop offset="0" class="wax-deep"/>
            <stop offset="0.4" class="wax"/>
            <stop offset="1" class="wax-deep"/>
          </linearGradient>
        </defs>
        <path d="M5 6.5h20v27.5q0 3.5-3.5 3.5h-13q-3.5 0-3.5-3.5z" fill="url(#jar-glass)"/>
        <ellipse cx="15" cy="9" rx="9.2" ry="2.3" fill="url(#jar-wax)"/>
        <path class="jar__wick" d="M15 9V5.6"/>
        <rect class="jar__label" x="5" y="17" width="20" height="11"/>
        <path class="jar__ink" d="M9 21.2h12M10.5 24.2h9"/>
        <path class="jar__gleam" d="M7.4 8.5v25"/>
        <ellipse class="jar__lip" cx="15" cy="6.5" rx="10" ry="2.2"/>
      </svg>
    </span>
  </template>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Cambia valori, colori e testi con le manopole e guarda l’animazione rispondere dal vivo. Oppure scrivi tu nel codice. Poi scarica la cartella con le tue modifiche.

33 valori da cambiare

Oppure chiedila a Claude Code

Col connettore di Animorio, Claude Code la prende da qui con tutta la sua guida e la adatta al tuo progetto. Incolla questa richiesta:

Prendi «Il pulsante della scatola di cartone» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco una pillola color espresso, alta 60 pixel, con un filo di luce in alto e un'ombra morbida sotto. A sinistra, in Bricolage Grotesque color panna, Add to cart e il prezzo €32 in una pillolina; nella punta tonda di destra una scatola di cartone ondulato aperta, girata verso di te e vista un poco dall'alto, così si guarda dentro: la faccia davanti più chiara con due frecce stampate (questo lato in su), il fianco destro in ombra, il fondo e le pareti di dentro più scuri, e le quattro alette aperte, quella davanti che pende in avanti, quella di destra in fuori, quella di sinistra e quella di dietro alzate. Il cartone ha le sue onde che si vedono appena e il bordo tagliato più chiaro sulle alette; la luce di ogni aletta cambia con il suo angolo. All'arrivo la scatola è chiusa e le alette si aprono di scatto una dopo l'altra, ondeggiando sulle loro molle. Col mouse sopra il pulsante la scritta si allarga, il fondo si gonfia con un rimbalzo e le alette fremono e si alzano un poco verso il puntatore, di più quella che lo guarda. Al clic arriva Packing… con un riflesso che la attraversa, e dall'alto cade un vasetto di candela di vetro ambrato con la cera chiara, lo stoppino e un'etichetta crema: entra nella scatola, si schiaccia un poco atterrando e la scatola cede sotto il peso. Poi le alette si chiudono una dopo l'altra, le due corte prima e le due lunghe sopra, e ognuna sbatte sulla scatola e rimbalza prima di fermarsi; un nastro adesivo terracotta scende sul fianco sinistro, corre lungo la fessura e scende sul destro, e la scatola fa un saltello e si schiaccia atterrando: in alto, piccolo, una spunta che si disegna e Added, sotto Ships tomorrow. Un altro clic fa passare una lama lungo il nastro, le alette si riaprono di scatto portandosi dietro le due metà tagliate, il nastro vecchio si stacca e cade dentro un'altra candela: questa volta la riga piccola dice 2 in cart. Con la tastiera il fuoco mostra un anello color espresso, la scritta si allarga e le alette sobbalzano; Invio o Spazio aggiungono. Al telefono il dito sul pulsante fa fremere le alette e il tocco aggiunge.

Come funziona

La scatola in CSS 3D

La scatola è un gruppo di span in transform-style: preserve-3d sotto una prospettiva di 360 pixel, girato di rotateX(-32deg) rotateY(-34deg). Il fondo, le quattro pareti e le quattro alette sono piani messi al loro posto con translateZ, translateX e rotateY dal centro della scatola; ogni piano ha due facce, il fuori e il dentro girato di mezzo giro, con backface-visibility: hidden, così da ogni lato si vede il cartone giusto. Le misure (--bw, --bd, --bh, --fl) stanno in :root e lo script le rilegge. Il cartone ondulato è un repeating-linear-gradient sottile sopra il colore kraft; la luce di ogni faccia è uno strato scuro con l'opacità --shade.

Le alette sulle molle

Ogni aletta è incernierata sul bordo alto della sua parete (transform-origin: 50% 100%) e gira con rotateX: 0 dritta in su, 90 chiusa sulla scatola, i numeri negativi aperta in fuori. Il suo angolo segue una molla smorzata calcolata a ogni fotogramma, a passi di 1/240 di secondo (SWING oscillazioni al secondo, DAMPING); per chiudere il bersaglio è un poco oltre i 90 gradi, quindi l'aletta sbatte sul fermo dei 90 e rimbalza tenendo una parte della velocità (BOUNCE), poi resta premuta. Le alette lunghe, quasi chiuse, si alzano di 0,9 pixel per stare sopra le corte. La luce del fuori è il prodotto scalare fra la sua normale (quella della parete girata verso l'alto dall'angolo) e la direzione della lampada.

Il fremito verso il puntatore

Ogni aletta sa da che parte guarda sullo schermo. Col puntatore sul pulsante il suo bersaglio si alza di 6 gradi più 22 per quanto guarda verso il puntatore, e ci si aggiunge un fremito di rumore a gradiente su due frequenze (9 e 23 al secondo), più forte per l'aletta che lo guarda; tutto moltiplicato per SHIVER e per quanto il puntatore è sul pulsante, che sale e scende con GSAP. Le molle fanno il resto: le alette tremano senza scatti.

La candela che cade dentro

La candela è un SVG (vetro ambrato in gradiente, cera, stoppino, etichetta) su un piano dentro lo spazio 3D della scatola, girato all'opposto della scatola (rotateY(34deg) rotateX(32deg)) così guarda sempre verso di te; il browser lo ordina in profondità con le pareti, quindi entrando sparisce dietro la faccia davanti. Cade con un'accelerazione (power2.in), si schiaccia e torna su un elastico, e la scatola cede nello stesso istante: uno scaleY dal suo fondo che torna con elastic.out. Ci sono due posti nel fondo: una candela nuova prende quello della più vecchia, che sta sotto le alette chiuse e sparisce senza che si veda.

Il nastro, il taglio e il saltello

Il nastro è fatto di quattro pezzi: due metà sulle alette lunghe, lungo il loro bordo libero, e due linguette sui fianchi. Le metà si allungano insieme con scaleX dopo la linguetta di sinistra e prima di quella di destra, e sembrano un nastro solo. Stando sulle alette, quando la scatola si riapre le due metà se ne vanno ognuna con la sua aletta, come un nastro tagliato; prima una lama chiara corre sulla fessura. La scatola salta su un livello suo (y −12 e ritorno con power2), e atterrando si schiaccia dal fondo; l'ombra sotto si stringe mentre è in aria.

Le parole e gli stati

Tre facce sovrapposte, larghe quanto le parole: a riposo con la copia larga invisibile per l'allargamento sotto il mouse (font-stretch da 86 a 100 per cento), Packing… col riflesso che la attraversa, e la faccia finale su due righe. Il pulsante è un button vero: aria-disabled mentre impacchetta, una zona aria-live dice Packing Fig & cedar candle… e poi Added. 1 in cart. Ships tomorrow., e l'aria-label dice che il clic dopo ne aggiunge un'altra. Il primo giro dura 2,1 secondi e quelli dopo 2,6, meno dei 2,8 fra un clic e l'altro dell'anteprima.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LABELscript.jsAdd to cartLa scritta a riposo. Se è troppo lunga per lo spazio, il carattere si rimpicciolisce.
PRICEscript.js€32Il prezzo, nella sua pillolina accanto alla scritta.
BUSYscript.jsPacking…La scritta mentre la candela entra e la scatola si chiude.
ADDEDscript.jsAddedLa riga piccola la prima volta, dopo la spunta.
UNITscript.jsin cartLa riga piccola dalla seconda volta, dopo il numero.
SHIPscript.jsShips tomorrowLa riga grande quando la scatola è chiusa: in un sito vero la data la dice il tuo negozio.
PRODUCTscript.jsFig & cedar candleIl prodotto, per la zona aria-live: in un sito vero è quello della pagina, e l'aggiunta va collegata al tuo carrello.
SWINGscript.js3.4Le oscillazioni al secondo delle alette sulle loro molle: più alto, cartone rigido e scatti svelti; più basso, alette molli e lente.
DAMPINGscript.js0.26Quanto le alette sono frenate: 0,08 sbattono e ondeggiano a lungo, 1 vanno dritte al loro posto.
BOUNCEscript.js0.38Quanta velocità tiene un'aletta quando sbatte sulla scatola: 0 si ferma di colpo, 0,8 rimbalza più volte.
SHIVERscript.js1Quanto le alette fremono e si alzano verso il puntatore: 0 restano ferme.
OPENscript.js[-72, -34, -44, -66]Gli angoli delle alette aperte, in gradi oltre la verticale: davanti, dietro, sinistra, destra. Oltre −60 un'aletta pende in fuori e mostra il suo dentro.

Provali nel Playground

Quando usarla

Per il pulsante Aggiungi al carrello di una bottega che spedisce: candele, ceramiche, tè, cosmetici, libri, cose fatte a mano. La scatola che si chiude racconta la spedizione, e la riga Ships tomorrow è il posto giusto per la data di consegna vera. Va collegato al carrello e alla data del tuo negozio: qui è tutto simulato.

Sul telefono

La pillola è larga circa 260 pixel: a 390 ci sta con margine, e se la scritta non ci sta il carattere si rimpicciolisce. Il dito sul pulsante fa fremere le alette verso di sé e il tocco aggiunge; la scatola resta della stessa misura.

Con il movimento ridotto

Niente molle né saltelli: le alette stanno ferme aperte, non fremono, il fondo non si gonfia e la scritta non si allarga (sotto il mouse il fondo si schiarisce appena). Al clic le facce della scatola svaniscono e tornano già chiuse, col nastro e la candela dentro, mentre Packing… e poi Added, Ships tomorrow arrivano in dissolvenza. Un altro clic fa lo stesso e il numero sale.

Trappole

  • Nel movimento ridotto la scatola svaniva con l'opacità del contenitore della prospettiva: un'opacità sotto 1 appiattisce il 3D, e per un attimo le facce si disegnavano nell'ordine del codice, con la scatola che sembrava chiusa. Ora svaniscono solo i piani piatti (le facce e le candele), e il 3D resta intero.
  • Sempre nel movimento ridotto le alette all'arrivo andavano verso la chiusura, perché il bersaglio di partenza delle molle era chiuso (con le animazioni la scatola entra chiusa e si apre). Ora senza animazioni il bersaglio è aperto e la molla è saltata: l'aletta va dritta al suo angolo.
  • L'aletta davanti aperta a −46 gradi, da questo punto di vista, si vedeva di taglio: una riga. Aperta a −72 pende in avanti e mostra il suo dentro, e quella di destra a −66 fa lo stesso sul fianco.
  • Le alette lunghe, chiuse sullo stesso piano delle corte, sfarfallavano (z-fighting). Si alzano di 0,9 pixel man mano che si chiudono, fra 55 e 88 gradi, e restano sopra.
  • Nella timeline di GSAP la posizione '+=0.14' conta dalla fine della timeline, non dall'ultimo add: le alette si chiudevano tardi e il giro durava 3,3 secondi, più dei 2,8 dell'anteprima. Ora ogni passo ha un tempo assoluto da un'origine B, che vale 0,5 secondi quando la scatola va prima riaperta.
  • Con la scatola vista a 27 gradi la candela dentro non si vedeva quasi: guardandola a 32 gradi si vede il fondo, e la candela ha la sua cima alla luce.

Librerie

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

Compressi con gzip, i tre file della demo pesano 13 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 12 ottobre 2026

  • Computer: Fatto
  • Telefono: Fatto
  • Movimento ridotto: Fatto
  • Safari su iPhone: Da fare

Licenza d’uso

Puoi usarla nei tuoi progetti e in quelli dei tuoi clienti. Non puoi rivenderla né ridistribuirla così com’è. GSAP resta sotto la sua licenza Standard. Licenza completa.

Tutti i componenti