
Il menu dalle tre righe
Menu, Transizioni, Fisica, Testo
NuovaGratis
La generazione di una fattura in PDF fatta come la composizione a caratteri mobili: premuto Generate invoice, i caratteri di piombo, con la lettera in rilievo e a rovescio, cadono uno dopo l’altro nel compositoio d’acciaio, si serrano con un piccolo colpo e ogni riga piena scende nel vantaggio d’ottone. Poi il rullo inchiostra la forma, il torchio si abbassa con un tonfo e il foglio si solleva e si gira: la fattura stampata dritta, con l’inchiostro vero. Una volta su due cade un carattere sbagliato, che salta fuori, e Retry rimette quello giusto.
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, viewport-fit=cover">
<title>The composing stick loader</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/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the composing stick loader: an invoice being generated, shown as a page of metal type being set by hand. Pressed,
the sorts drop one after the other into the steel composing stick, their letters raised and back to front; a
full line is tightened with a tap and slides down into the brass galley. When the forme is complete a roller
inks it, the platen comes down with the sheet and lifts, and the sheet turns over with the invoice printed the
right way round. The metal is drawn in HTML and CSS by script.js, the print is painted on a canvas -->
<section class="press" aria-label="Invoice generation" data-press>
<div class="press__scene" role="progressbar" aria-label="Invoice generation" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Ready to generate" data-scene>
<div class="press__fit" data-fit>
<div class="press__box" aria-hidden="true" data-box>
<div class="galley">
<div class="galley__floor"></div>
<div class="galley__rim galley__rim--top"></div>
<div class="galley__rim galley__rim--side"></div>
<div class="galley__rim galley__rim--foot"></div>
</div>
<div class="stick">
<div class="stick__back"></div>
<div class="stick__bed"></div>
<div class="stick__clamp"></div>
<div class="stick__knee"></div>
</div>
<div class="press__type" data-type></div>
<div class="stick__lip"></div>
<div class="roller" data-roller><div class="roller__drum" data-drum></div></div>
<div class="sheet__shadow" data-sheet-shadow></div>
<div class="sheet" data-sheet>
<canvas class="sheet__face sheet__face--back" data-back></canvas>
<canvas class="sheet__face sheet__face--front" data-front></canvas>
</div>
<div class="platen" data-platen><div class="platen__ribs"></div></div>
<div class="press__fly" data-fly></div>
</div>
</div>
</div>
<p class="press__status" aria-hidden="true">
<svg class="press__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.2 4.2 8.8-9.4" pathLength="1"/></svg>
<svg class="press__warn" viewBox="0 0 24 24" data-warn><path d="M12 7.5v6M12 16.6v.1" pathLength="1"/><path d="M10.3 3.9L2.6 17.6a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>
<span class="press__swap" data-status>Ready to generate</span>
<span class="press__pct" data-pct></span>
</p>
<div class="press__buttons">
<button class="pill is-quiet" type="button" hidden data-again><span class="pill__label">Generate again</span></button>
<button class="pill" type="button" data-go data-autoclic><span class="pill__label" data-label>Generate invoice</span></button>
</div>
<p class="sr" role="status" 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>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.
47 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 compositoio dei caratteri» da Animorio e adattala al mio sitoOgni carattere è largo esattamente quanto l’avanzamento della sua lettera, misurato con measureText su un canvas nel carattere variabile Fraunces, al peso della sua riga (720 per il titolo, 470 per il cliente…). La riga si compone dalla squadra verso sinistra nell’ordine di lettura, così la forma è lo specchio della pagina; se una riga non sta nella giustezza, la dimensione scende finché ci sta. Quello che resta fino al cursore si riempie di quadrati, blocchetti più bassi senza lettera.
Ogni carattere è un div con due facce: l’occhio in alto, alto 22 pixel, con un gradiente inclinato verso la luce e un bordo chiaro in alto a sinistra, e il fusto davanti, più scuro, con la tacca disegnata da due righe di gradiente e un’ombra. La lettera sta sull’occhio con scaleX(-1), in rilievo grazie a tre text-shadow (luce in alto a sinistra, ombra in basso a destra, ombra morbida). Ogni carattere ha una luminosità sua, fra 0,9 e 1,08, come i caratteri consumati; il riflesso della serrata è un ::after in soft-light che scorre grazie a una variabile CSS animata da GSAP.
Una sola timeline GSAP tiene tutto: le cadute (ognuna gira verso dritto con power2.in e finisce un filo larga), il sobbalzo della riga con elastic, i quadrati, la serrata, il viaggio della riga nel vantaggio con un rimbalzo, il rullo, la platina, il foglio. La percentuale viene da callback messe nel momento in cui ogni carattere tocca il fondo. Nella generazione che va storta il carattere sbagliato entra nella timeline prima di quello giusto, seguito da un addPause: la timeline si ferma da sola, e Retry la fa ripartire.
Il rullo scende sul vantaggio con power1.inOut, e il momento in cui il suo centro passa su ogni riga si trova invertendo la curva: lì la riga prende la classe che fa nere le lettere. La platina scende da più grande e più in alto (più vicina all’occhio) con power3.in, e all’arrivo tutta la scena scende di due pixel e torna su con una molla. Il foglio ha due facce in CSS 3D con backface-visibility: girarlo di 180 gradi sull’asse verticale rovescia di nuovo lo specchio, così la stampa si legge dritta; l’ombra sotto si stringe e si riallarga mentre gira.
La pagina si dipinge una volta per lavoro, dalle stesse larghezze dei caratteri specchiate intorno al centro: la carta (grana pixel per pixel su un canvas a parte, qualche fibra, un’ombra ai bordi), il solco dell’impressione (la lettera in chiaro spostata in basso a destra e in scuro in alto a sinistra), poi l’inchiostro su un canvas suo, con un’opacità diversa per lettera, il bordo spremuto dato da un contorno sottile e migliaia di puntini tolti con destination-out, unito alla carta in multiply. Il dorso è la stessa carta con le lettere a rovescio appena visibili.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
SECONDS | script.js | 6 | Quanto dura la composizione delle righe, in secondi; l’inchiostrazione e la stampa aggiungono circa due secondi (una vera generazione segue il lavoro del server). |
LINES | script.js | ['INVOICE INV-0412', 'Marlow & Finch', 'Due 30 October', 'Total €1,840.00'] | Le righe della fattura, al massimo quattro: una riga troppo lunga per il compositoio rimpicciolisce da sola. |
WEIGHTS | script.js | [720, 470, 420, 640] | Il peso del carattere di ogni riga, da 100 a 900: il carattere è variabile, quindi ogni peso è vero, anche nella stampa. |
FAIL_EVERY | script.js | 2 | Nella demo, una generazione ogni quante va storta (0 mai). |
FAIL_LINE | script.js | 3 | In quale riga cade il carattere sbagliato. |
FAIL_SORT | script.js | ['b', 'd'] | Il carattere giusto e quello sbagliato che cade al suo posto; se nella riga non c’è il giusto, sbaglia una lettera a metà riga. |
LABELS | script.js | { busy: 'Generating…', done: 'Download PDF', saved: 'Downloaded', retry: 'Retry' } | Le scritte del pulsante mentre lavora, alla fine, dopo lo scaricamento e dopo l’errore; quella a riposo, Generate invoice, si cambia in index.html. |
WORDS | script.js | { set: 'Laying out INV-0412', render: 'Rendering PDF', done: 'Invoice ready', fail: 'Glyph mismatch on line' } | La riga sotto il disegno mentre compone (con la percentuale), mentre stampa, alla fine e dopo l’errore (seguita dal numero della riga); quella a riposo, Ready to generate, si cambia in index.html. |
Per un’attesa di qualche secondo che finisce con un documento: generare una fattura, un preventivo, una ricevuta o un contratto in PDF, esportare un report impaginato, preparare un file da stampare. Sta bene in un gestionale per freelance e studi, in un’app di fatturazione o di editoria, dove la tipografia fa parte del prodotto. Per un’attesa di un attimo è troppo lungo.
Il disegno è largo 360 pixel e si scala alla larghezza che ha: a 390 resta intero con 16 pixel per lato, e le lettere restano nitide perché sono testo vero; il canvas della stampa dipinge almeno a due volte la densità. Sopra c’è spazio per i caratteri che cadono. Le pillole sono alte 44 e col dito si schiacciano e rimbalzano; il carattere che aspetta sopra la squadra c’è col mouse e con la tastiera.
Niente cade e niente si sposta: ogni riga compare in dissolvenza già al suo posto nel vantaggio, una dopo l’altra, le lettere diventano nere riga per riga senza rullo, e alla fine il foglio stampato compare in dissolvenza già dritto, senza torchio e senza giro. Il carattere sbagliato si colora di rosso e svanisce. Percentuale, parole e spunta si aggiornano con dissolvenze.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.
Compressi con gzip, i tre file della demo pesano 15 KB; le librerie: GSAP 28 KB.
Provata il 8 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.