
Il menu sotto il foglio
Menu, 3D, Fisica, Transizioni
NuovaGratis
Il menu di un piccolo marchio di candele come una scatola di fiammiferi: chiusa è il pulsante, premuta si alza verso di te e il cassettino esce dalla scatola a scatti, con l’attrito del cartone, fino a sbattere contro il bordo. Ogni fiammifero è una sezione, col nome stampato sul bastoncino. Scelto, la mano lo tira fuori, lo sfrega sulla striscia ruvida tra le scintille, la capocchia si accende in una fiammata e una piccola fiamma disegnata a ogni fotogramma scalda la scatola; poi si spegne in un filo di fumo e il fiammifero torna al suo posto bruciato: è la pagina in cui sei.
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 matchbox menu</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full-italic.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/sofia-sans-condensed@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the menu of a candle maker as a box of matches. Closed, the button is the box: the printed sleeve, the brown
striker along its sides. Pressed, the box is picked up and the tray slides out of the sleeve; inside lie the
matches, one per section, each with the name printed along its stick. Chosen, a match is taken out, struck on
the side of the box, burns for a moment and goes back into its place burnt: the burnt match is the page you
are on. The flame, the sparks, the smoke and the light are drawn by the script on a canvas -->
<nav class="mb" aria-label="Mothlight" data-matchbox>
<span class="mb__frame" aria-hidden="true" data-frame></span>
<div class="mb__box" data-box>
<button class="sleeve" type="button" aria-expanded="false" aria-controls="mb-sections" data-sleeve data-autoclic>
<!-- the top side of the box, with its striker -->
<span class="sleeve__side" aria-hidden="true">
<svg class="sleeve__grain" viewBox="0 0 196 13" preserveAspectRatio="none">
<defs>
<filter id="mb-sand" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.55 0.7" numOctaves="2" seed="7"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.08 0 0 0 0 0.05 0 0 0 0 0.03 0 0 0 -2.6 1.55"/>
<feComposite in2="SourceGraphic" operator="in"/>
</filter>
<filter id="mb-sand-light" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.8 0.9" numOctaves="1" seed="21"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.86 0 0 0 0 0.66 0 0 0 -3.2 1.62"/>
<feComposite in2="SourceGraphic" operator="in"/>
</filter>
</defs>
<rect width="196" height="13" filter="url(#mb-sand)"/>
<rect width="196" height="13" filter="url(#mb-sand-light)"/>
</svg>
</span>
<!-- the label printed on the sleeve -->
<span class="sleeve__face" aria-hidden="true">
<svg class="label" viewBox="0 0 196 132">
<defs>
<filter id="mb-paper" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" seed="3"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.36 0 0 0 0 0.25 0 0 0 0 0.12 0 0 0 0.22 -0.05"/>
<feComposite in2="SourceGraphic" operator="in"/>
</filter>
<radialGradient id="mb-age" cx="0.42" cy="0.4" r="0.75">
<stop offset="0.55" stop-color="#5a3a14" stop-opacity="0"/>
<stop offset="1" stop-color="#5a3a14" stop-opacity="0.2"/>
</radialGradient>
</defs>
<rect class="label__paper" width="196" height="132"/>
<rect width="196" height="132" filter="url(#mb-paper)"/>
<rect width="196" height="132" fill="url(#mb-age)"/>
<rect class="label__rule" x="6.5" y="6.5" width="183" height="119" rx="3"/>
<rect class="label__rule label__rule--fine" x="9.6" y="9.6" width="176.8" height="112.8" rx="1.6"/>
<path class="label__rule label__rule--fine" d="M18 38.5H112"/>
<!-- the moth, drawn to the light -->
<g class="moth" transform="translate(150 67)">
<g class="moth__half">
<path class="moth__wing" d="M-2.2-3.6C-9-15.5-24.6-25.4-34.4-20.6-38.8-16.4-35.6-7.6-27.6-2.4-19.6 2-9.6 2.4-2.2 1.2Z"/>
<path class="moth__wing" d="M-2.2 1.6C-9.6 3.4-21.6 8.6-22.6 16.4-22 22.4-13 23-7.6 17.2-4.2 13.4-2.4 7.8-2.2 3.4Z"/>
<path class="moth__vein" d="M-30.4-15.6C-23.6-12.6-15.4-8.4-5.4-2.2M-27.6-6C-20.2-5-13.4-2.8-5.6-.4M-17.4-19.6C-15-13.4-11.6-7.6-6.2-2.8"/>
<path class="moth__eye" d="M-22.4-9.6a4.2 4.2 0 1 1 6.4 3.4"/>
<path class="moth__vein" d="M-18.4 15.4C-14 11.6-9.6 7.6-4.8 4.2M-13.6 19.4C-10.6 14.4-7.6 9.4-4.6 5.4"/>
<path class="moth__feeler" d="M-1.2-13.4C-4.6-20-9.4-24-15.2-25.6M-6.6-21.4-8.6-19.4M-9.4-23.2-11 -21M-12.4-24.6-13.4-22.2"/>
</g>
<g class="moth__half" transform="scale(-1 1)">
<path class="moth__wing" d="M-2.2-3.6C-9-15.5-24.6-25.4-34.4-20.6-38.8-16.4-35.6-7.6-27.6-2.4-19.6 2-9.6 2.4-2.2 1.2Z"/>
<path class="moth__wing" d="M-2.2 1.6C-9.6 3.4-21.6 8.6-22.6 16.4-22 22.4-13 23-7.6 17.2-4.2 13.4-2.4 7.8-2.2 3.4Z"/>
<path class="moth__vein" d="M-30.4-15.6C-23.6-12.6-15.4-8.4-5.4-2.2M-27.6-6C-20.2-5-13.4-2.8-5.6-.4M-17.4-19.6C-15-13.4-11.6-7.6-6.2-2.8"/>
<path class="moth__eye" d="M-22.4-9.6a4.2 4.2 0 1 1 6.4 3.4"/>
<path class="moth__vein" d="M-18.4 15.4C-14 11.6-9.6 7.6-4.8 4.2M-13.6 19.4C-10.6 14.4-7.6 9.4-4.6 5.4"/>
<path class="moth__feeler" d="M-1.2-13.4C-4.6-20-9.4-24-15.2-25.6M-6.6-21.4-8.6-19.4M-9.4-23.2-11 -21M-12.4-24.6-13.4-22.2"/>
</g>
<path class="moth__body" d="M0-13.2C1.9-13.2 2.9-10.4 2.9-6.4 2.9 1 2.4 9.6 0 15.4-2.4 9.6-2.9 1-2.9-6.4-2.9-10.4-1.9-13.2 0-13.2Z"/>
<path class="moth__rings" d="M-2.4-1.4H2.4M-2.3 2.6H2.3M-2 6.6H2M-1.5 10.4H1.5"/>
</g>
</svg>
<span class="label__brand">Mothlight</span>
<span class="label__word" data-word><span class="label__w">Menu</span></span>
<span class="label__line">Candle Co. · Hand-poured</span>
</span>
<!-- the bottom side, just showing -->
<span class="sleeve__edge" aria-hidden="true"></span>
<span class="sr">Menu</span>
</button>
<!-- the sleeve's shadow on the tray where it comes out -->
<span class="mb__shade" aria-hidden="true" data-shade></span>
<!-- the tray and the matches: each match is a section of the site -->
<div class="tray" data-tray>
<span class="tray__floor" aria-hidden="true"></span>
<ul class="tray__list" id="mb-sections" aria-label="Sections" data-list>
<li class="slot" data-slot>
<a class="match" href="#candles" aria-current="page" data-match>
<span class="match__art"><span class="match__stick"></span><span class="match__name">Candles</span><span class="match__head"><i class="match__sear"></i></span></span>
</a>
</li>
<li class="slot" data-slot>
<a class="match" href="#gift-sets" data-match>
<span class="match__art"><span class="match__stick"></span><span class="match__name">Gift sets</span><span class="match__head"><i class="match__sear"></i></span></span>
</a>
</li>
<li class="slot" data-slot>
<a class="match" href="#workshops" data-match data-autoclic>
<span class="match__art"><span class="match__stick"></span><span class="match__name">Workshops</span><span class="match__head"><i class="match__sear"></i></span></span>
</a>
</li>
<li class="slot" data-slot>
<a class="match" href="#our-story" data-match>
<span class="match__art"><span class="match__stick"></span><span class="match__name">Our story</span><span class="match__head"><i class="match__sear"></i></span></span>
</a>
</li>
<li class="slot" data-slot>
<a class="match" href="#stockists" data-match data-autoclic>
<span class="match__art"><span class="match__stick"></span><span class="match__name">Stockists</span><span class="match__head"><i class="match__sear"></i></span></span>
</a>
</li>
</ul>
<span class="tray__rim" aria-hidden="true"></span>
</div>
<!-- the hand: the match that is taken out flies here -->
<div class="hand" aria-hidden="true" data-hand></div>
</div>
<!-- the light of the flame on the box (a light map, in multiply), then the flame, the sparks, the smoke and the
glow (added in screen) -->
<canvas class="fx fx--shade" aria-hidden="true" data-fx-shade></canvas>
<canvas class="fx" aria-hidden="true" data-fx></canvas>
</nav>
<p class="sr" aria-live="polite" data-say></p>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
32 valori da cambiare
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 menu a scatola di fiammiferi» da Animorio e adattala al mio sitoTutto quello che sta nella scatola è disegnato nei suoi pixel (etichetta in SVG con la grana della carta e della striscia ruvida fatta da feTurbulence, cassettino, fiammiferi in CSS) e lo script scala l’intera scatola: chiusa è alta 60 pixel su ogni schermo, aperta è grande come il disegno. Aprendo, una timeline di GSAP la porta verso di te con un back.out e una rotazione di due gradi che si raddrizza, come presa in mano.
Il pollice spinge il cassettino attraverso una molla rigida (la pelle e il cartone che cedono). Finché la spinta non supera l’attrito statico il cassettino resta fermo; poi scivola con l’attrito dinamico, più basso, supera un poco il pollice, rallenta e fa di nuovo presa: è lo stick-slip, a passi di 1/240 di secondo con il fotogramma limitato a 1/30. Al bordo il cassettino rimbalza con BOUNCE della sua velocità, e i fiammiferi, appoggiati sciolti, sentono la sua accelerazione al contrario: scivolano e ruotano un poco nella corsia e tornano su una molla.
Il fiammifero scelto viene copiato in un livello «mano» sopra la scatola, in CSS 3D: le dita sono il perno, rotateZ lo gira, rotateY ne alza la capocchia verso di te, e la capocchia è sempre rivolta a chi guarda (una rotazione al contrario). La mano lo tira fuori lungo la sua corsia, inclinato quasi verso di te, finché la coda ha superato il nome più lungo delle corsie sopra (misurato quando il carattere è arrivato); solo allora sale, passando sopra le capocchie. Finché la coda è dentro la scatola, un clip-path con un buco grande come l’etichetta la nasconde sotto il cartone.
Sul canvas la fiamma è una goccia (x = sin t · sin^m(t/2), y = cos t) disegnata in quattro strati sommati in modalità lighter: un alone senza bordi, il corpo dal blu all’arancio, il cuore bianco, la coppa blu alla radice. Altezza e larghezza tremano su un rumore liscio, la punta ondeggia di più e si piega su una molla al contrario della velocità della capocchia. Le scintille sono strisce che cadono e si raffreddano dal bianco al rosso, lo zolfo sono sbuffi che si allargano, il fumo è una fila di punti che salgono su un campo di rumore comune ai vicini e si uniscono in una curva doppia, alone e filo.
Due canvas sopra la scatola, ritagliati sulla sua forma: uno in multiply è una mappa di luce, che porta la scatola in una penombra calda lontano dalla fiamma e la lascia chiara e dorata vicino; l’altro in screen aggiunge il bagliore. Così la luce cade sull’etichetta e sui fiammiferi vicini e il nero della pagina resta nero. Quando niente brucia, non fuma e non scintilla, i canvas si puliscono una volta e non si ridisegnano più.
La scatola è un button con aria-expanded e aria-controls, i fiammiferi sono link in una lista con aria-current sul bruciato, inert a scatola chiusa. Frecce, Home, End, Invio, Esc che chiude e riporta il fuoco; il fuoco si vede solo da tastiera. Una scelta mentre un fiammifero brucia accorcia la fiamma e accelera la mano, chiudere aspetta che il fiammifero sia tornato. Il clic dell’anteprima su un fiammifero a scatola chiusa la apre e lo accende quando il cassettino sbatte contro il bordo.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
FRICTION | script.js | 1 | Quanto il cassettino fa presa e scivola mentre esce (0 scorre liscio, 1,6 va a scatti). |
PUSH | script.js | 260 | Quanto veloce il pollice spinge il cassettino, in pixel al secondo. |
BOUNCE | script.js | 0.35 | Quanta velocità tiene il cassettino quando rimbalza sul bordo (0 nessuna, 0,7 vivace). |
PACE | script.js | 1 | Il passo della mano che tira fuori il fiammifero, lo sfrega e lo rimette a posto. |
BURN | script.js | 1.2 | I secondi in cui il fiammifero brucia prima che la mano lo scuota. |
FLAME | script.js | [52, 1] | La fiamma: la sua altezza in pixel, e quanto trema (0 ferma, 2 inquieta). |
SPARKS | script.js | 18 | Le scintille di uno sfregamento. |
GLOW | script.js | 0.6 | Quanta luce getta la fiamma sulla scatola (0 nessuna, 1 molta). |
SPENT | script.js | 'current' | Quali fiammiferi restano bruciati: 'current' solo quello della sezione in cui sei, 'visited' tutti quelli accesi. |
BY_HAND | script.js | true | Un fiammifero trascinato veloce sulla striscia ruvida si accende da solo. |
OPEN_WORD | script.js | 'Close' | Che cosa dice l’etichetta mentre la scatola è aperta. |
Per il menu di un sito con un carattere da oggetto e un po’ di calore: un produttore di candele, un cocktail bar, una tabaccheria, un ristorante, una distilleria, un negozio di oggetti per la casa. Da quattro a sei sezioni dai nomi brevi; il fiammifero che brucia è anche il momento in cui la pagina nuova si carica.
La scatola chiusa resta alta 60 pixel; aperta, si stringe sulla larghezza dello schermo (a 390 pixel è a circa nove decimi) e niente esce di lato. Il dito che preme un fiammifero lo solleva e lo fa rotolare come il puntatore, e lasciandolo lo sceglie; trascinandolo lo si può accendere sulla striscia.
Niente corse né rimbalzi: la scatola si dissolve chiusa e ricompare aperta. Scegliendo un fiammifero, questo non si muove: una fiamma ferma compare sulla sua capocchia con la sua luce sulla scatola, il nome si scalda, poi la fiamma si dissolve e il fiammifero diventa bruciato. Sotto il puntatore il fiammifero si schiarisce invece di sollevarsi.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Sofia Sans Condensed (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 21 KB; le librerie: GSAP 28 KB.
Provata il 7 ottobre 2026
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.
Con l’email e la password del tuo account.
oppure con l’email
Hai fatto l’accesso come .
Con l’account scarichi lo zip di ogni animazione e di ogni pacchetto, gratis.