Vai al contenuto
animorio
EN
Torna alla libreria

La scatola di praline da aprire

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

L’intestazione di una cioccolateria inventata, nello stile ricco: una scatola di nove praline costruita in 3D, verde petrolio con la stampa a caldo d’oro e il nastro di raso rosa annodato in un fiocco, che si inclina verso il puntatore. Un clic scioglie il nastro e apre il coperchio sulla cerniera; la pralina sotto il puntatore, o sotto il dito, si solleva dal suo pirottino girando, con uno sbuffo di cacao, e il suo nome compare sul cartoncino.

Il codice

Una cartella che funziona da sola: index.html, style.css, script.js e le librerie da CDN. Nelle schede React e Vue 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 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>

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.

63 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 «La scatola di praline da aprire» da Animorio e adattala al mio sito

Cosa fa

Maison Noix è una cioccolateria. A destra del titolo «Nine little reasons.» c’è la sua scatola del sabato, appoggiata su un tavolo rosa cipria con un cartoncino del menu, due briciole di pistacchio e qualche granella di cacao. La scatola è costruita in 3D con il fondo, quattro pareti che hanno un lato esterno verde petrolio e uno interno di carta crema, e un coperchio con i bordi che scendono sopra le pareti; sul coperchio c’è il marchio stampato a caldo in oro, che luccica dove arriva la luce, e un nastro di raso rosa che incrocia il coperchio, scende lungo i bordi e si annoda in un fiocco con le code. Muovendo il puntatore la scatola si inclina su una molla, e i riflessi del coperchio, dell’oro e delle praline si spostano dalla parte opposta; lasciata sola, respira piano. Un clic (o un tocco, o Invio) scioglie il nastro: il fiocco salta su e sparisce, le due strisce scivolano via; poi il coperchio si apre sulla cerniera di dietro, va un poco oltre e si assesta, e dentro si vede la carta velina e le nove praline nei pirottini pieghettati, che fanno un piccolo salto una dopo l’altra. Ogni pralina ha il suo corpo arrotondato e la sua decorazione: il caramello salato con i fili d’oro e i fiocchi di sale, il lampone rosa con la spirale bianca, il pistacchio, la nocciola, il grand cru con la foglia d’oro, il chicco di caffè, il cocco, il petalo di rosa e la scorza d’arancia. Passando sopra una pralina (o toccandola) questa si solleva dal pirottino girando un poco, con uno sbuffo di cacao, e il cartoncino sotto il titolo dice il suo nome e le note. Un altro clic richiude il coperchio, che atterra con un piccolo colpo, e il nastro si riannoda col fiocco che cade elastico.

Come funziona

Una scatola fatta di piani

Tutto sta in un contenitore con transform-style: preserve-3d, inclinato con rotateX da un elemento e girato con rotateZ dal suo figlio (in quest’ordine, se no la scatola gira attorno allo schermo e non attorno a sé). Le pareti sono div ruotati di 90 gradi attorno al loro bordo; ognuna ha due facce a 2 pixel di distanza, verde fuori e crema dentro, così si vede quella giusta da ogni parte. Le misure sono variabili CSS calcolate al resize, quindi la scatola si ridisegna anche a metà animazione.

Il coperchio sulla cerniera

Il coperchio è un gruppo sollevato all’altezza delle pareti, con transform-origin sul bordo di dietro: aprirlo è una rotazione di rotateX fino a 112 gradi, con un piccolo sorpasso a 116 e un ritorno a 108 prima di fermarsi. Sopra c’è il piano esterno con la stampa e il nastro, sotto il piano interno girato di 180 gradi; tutti e due hanno backface-visibility: hidden, così si vede solo quello rivolto a chi guarda.

Praline con un corpo

Ogni pralina è una pila di sei fette rotonde, sempre più strette verso l’alto, spostate con translateZ: inclinando la scatola si vede il fianco arrotondato. In cima c’è la faccia con la decorazione in SVG, disegnata con un generatore di numeri a seme fisso perché sia uguale a ogni visita, e un riflesso che si sposta al contrario del puntatore.

Sollevare e sbuffare

Il punto sotto il puntatore si trova con elementFromPoint, che rispetta le trasformazioni 3D. La pralina sale con un tween di z e rotationZ con back.out, l’ombra nel pirottino si allarga e schiarisce, e sette granelli di cacao partono dal centro in cerchio sul piano del vassoio e salgono spegnendosi.

Valori da cambiare

NomeFileDi partenzaCosa cambia
PRALINESscript.jsnove pralineLe nove praline, in ordine di lettura: nome, note sul cartoncino e decorazione (drizzle, swirl, nuts, hazelnut, gold, bean, dust, petal, orange).
BRANDscript.jsMaison NoixIl nome nella barra e in oro sul coperchio.
TILTscript.js1Quanto si inclina la scatola verso il puntatore: 0 per niente, 2 tanto.
OPEN_TIMEscript.js0.9I secondi che il coperchio impiega ad aprirsi.
LIFTscript.js1Quanto sale una pralina dal suo pirottino.
SPINscript.js1Quanto gira una pralina sollevata.
PAUSEscript.js1.4I secondi fra una mossa e l’altra quando la scatola gioca da sola.
--box, --gold, --ribbon, --dark, --milk…style.cssverde petrolio, oro, rosaI colori in :root: la scatola, l’oro, il nastro e i tre cioccolati delle praline.

Provali nel Playground

Quando usarla

Per l’intestazione di una cioccolateria, una pasticceria, un negozio di regali o di dolci, la pagina di un prodotto da regalare o una scatola in abbonamento. È una vetrina: i prezzi e il pulsante per comprare vanno anche in testo normale.

Sul telefono

Stessa scatola e stesse animazioni, misure adattate: il titolo e il cartoncino in alto, la scatola sotto, grande quanto lo spazio che resta. Un tocco apre e chiude, un tocco su una pralina la solleva, e trascinando il dito la scatola si inclina; il palco ha touch-action none.

Con il movimento ridotto

Niente movimento: la scatola è ferma e già aperta, senza nastro; la pralina scelta si riconosce perché le altre si attenuano in dissolvenza, e il cartoncino cambia con una dissolvenza.

Trappole

  • L’ordine delle rotazioni conta: GSAP scrive rotate prima di rotateX, quindi inclinazione e giro stanno su due elementi annidati.
  • Un elemento con figli in 3D (preserve-3d) e backface-visibility: hidden si spezza male in Chrome: il piano del coperchio deve restare piatto, col nastro dentro.
  • Il fiocco va sollevato un poco sopra il piano del coperchio (z), se no ci finisce sotto.
  • GSAP sovrascrive il transform del CSS: la posizione dei gruppi (l’altezza del coperchio, del vassoio) sta su un genitore, la rotazione animata sul figlio.

Librerie

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

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

Collaudo

Provata il 23 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.

Tutte le animazioni