Vai al contenuto
animorio
EN
Tutti i componenti

Il modulo scritto in una frase

Moduli, Testo, TransizioniGSAP, Solo CSSImpegnativaPeso compresso: Demo 11 KBGSAP 28 KBGratisScarica lo zip

Nello stile minimal: la richiesta di preventivo di uno studio di design scritta come una frase sola, Hello, I’m … and I’d like … Ogni spazio vuoto è un campo vero che si allarga mentre scrivi, e le parole dopo di lui si fanno da parte su una molla loro; una parola spinta alla riga dopo aspetta che la riga le faccia posto e ci cade dentro dall’alto. Inviata, le righe sotto gli spazi si chiudono, quello che hai scritto prende peso e una riga blu firma la frase.

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 form written as a sentence</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/fraunces@5.3.0/full-italic.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="stage">
    <!-- the enquiry form of a design studio written as one sentence: every blank is a real field that grows with
         what you write, and the words after it make room on a spring. Sending is pretended: see post() in the script -->
    <form class="frase" action="#" novalidate aria-labelledby="frase-title" data-frase data-autoclic>
      <h2 class="frase__eyebrow" id="frase-title">Start a project</h2>

      <p class="frase__text" data-text>Hello, I’m <span class="tok" data-tok><span class="blank" data-blank="name"><label class="sr" for="frase-name">Your name</label><input class="blank__input" id="frase-name" name="name" type="text" autocomplete="name" autocapitalize="words" spellcheck="false" enterkeyhint="next" placeholder="your name" maxlength="40" aria-describedby="frase-name-err" aria-invalid="false" data-input><span class="blank__mirror" aria-hidden="true" data-mirror></span><span class="blank__caret" aria-hidden="true" data-caret></span><span class="blank__line" aria-hidden="true" data-line></span><span class="sr" id="frase-name-err" data-err></span></span>,</span> and I’d like <span class="tok" data-tok><span class="blank blank--choice" data-blank="kind"><label class="sr" for="frase-kind">What you need</label><select class="blank__select" id="frase-kind" name="kind" data-select>
            <option>a new website</option>
            <option>a brand identity</option>
            <option>an online shop</option>
            <option>a book design</option>
          </select><span class="blank__face" aria-hidden="true" data-face><span class="face__word" data-word></span><svg class="face__chev" viewBox="0 0 12 12" aria-hidden="true"><path d="M2.5 4.5 6 8l3.5-3.5"/></svg></span><span class="blank__line" aria-hidden="true" data-line></span></span></span> for <span class="tok" data-tok><span class="blank" data-blank="work"><label class="sr" for="frase-work">What you do</label><input class="blank__input" id="frase-work" name="work" type="text" autocomplete="organization" spellcheck="true" enterkeyhint="next" placeholder="what you do" maxlength="48" aria-describedby="frase-work-err" aria-invalid="false" data-input><span class="blank__mirror" aria-hidden="true" data-mirror></span><span class="blank__caret" aria-hidden="true" data-caret></span><span class="blank__line" aria-hidden="true" data-line></span><span class="sr" id="frase-work-err" data-err></span></span>.</span> My budget is <span class="tok" data-tok><span class="blank blank--choice" data-blank="budget"><label class="sr" for="frase-budget">Your budget</label><select class="blank__select" id="frase-budget" name="budget" data-select>
            <option>under €5k</option>
            <option selected>€5–10k</option>
            <option>€10–25k</option>
            <option>over €25k</option>
          </select><span class="blank__face" aria-hidden="true" data-face><span class="face__word" data-word></span><svg class="face__chev" viewBox="0 0 12 12" aria-hidden="true"><path d="M2.5 4.5 6 8l3.5-3.5"/></svg></span><span class="blank__line" aria-hidden="true" data-line></span></span>,</span> and you can write back to me at <span class="tok" data-tok><span class="blank" data-blank="email"><label class="sr" for="frase-email">Your email</label><input class="blank__input" id="frase-email" name="email" type="text" inputmode="email" autocomplete="email" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="send" placeholder="your email" maxlength="80" aria-describedby="frase-email-err" aria-invalid="false" data-input><span class="blank__mirror" aria-hidden="true" data-mirror></span><span class="blank__caret" aria-hidden="true" data-caret></span><span class="blank__line" aria-hidden="true" data-line></span><span class="sr" id="frase-email-err" data-err></span></span>.</span></p>

      <span class="frase__rule" aria-hidden="true" data-rule></span>

      <div class="frase__foot">
        <button class="send" type="submit" data-send>
          <span class="send__bg" aria-hidden="true"></span>
          <span class="send__label">Send enquiry</span>
        </button>
        <div class="frase__note" data-note><span class="note__line">We answer every enquiry within two days.</span></div>
      </div>
      <p class="sr" role="status" aria-live="polite" data-live></p>
    </form>
  </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.

41 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 modulo scritto in una frase» da Animorio e adattala al mio sito

Cosa fa

Su una pagina bianca, sotto la piccola scritta Start a project, una frase grande con le grazie: Hello, I’m your name, and I’d like a new website for what you do. My budget is €5–10k, and you can write back to me at your email. Le parole in corsivo grigio sono gli spazi da riempire, ognuno con una riga sottile sotto; a new website e €5–10k sono due scelte con una piccola freccia. Sotto c’è la pillola nera Send enquiry e accanto We answer every enquiry within two days. Scrivendo in uno spazio, la riga sotto si fa nera, lo spazio resta un poco più lungo di quello che hai scritto e quando la scrittura ci arriva si allunga in un colpo: le parole dopo scivolano a destra su una molla e si posano con un pixel di troppo; quella che non ci sta più sparisce dalla fine della riga, la riga sotto le apre uno spazio e lei ci cade dentro dall’alto. Lasciando lo spazio si richiude intorno alla parola e le altre tornano indietro. Cambiando una scelta la parola vecchia sale via, la nuova sale da sotto e lo spazio si apre o si chiude seguito dalle parole. Premendo Send enquiry con qualcosa che manca, quello spazio diventa blu, fa un piccolo scatto sulla sua molla e accanto al pulsante la frase continua: Almost there. Add an email to write back to., con il nome di ogni spazio mancante in blu da toccare per tornarci. Con tutto a posto le righe sotto gli spazi si chiudono verso il centro una dopo l’altra, le frecce svaniscono, le parole scritte diventano più pesanti e la frase si riassesta; una riga blu la sottolinea da sinistra a destra e il pulsante diventa Sent, con accanto Thanks, Mira: we’ll write back within two days. e Edit per tornare a scrivere.

Come funziona

Una frase fatta di gettoni

Le parole della frase vengono divise dallo script in span inline-block (i gettoni), con gli spazi fra loro: la frase va a capo come un testo normale. Ogni spazio da riempire è un input vero (o un select vero sotto le parole che mostra), con la sua label per i lettori di schermo, aria-invalid e aria-describedby al suo errore, chiuso in un gettone insieme alla virgola o al punto che lo segue, così la punteggiatura non va mai a capo da sola.

FLIP con una molla per ogni parola

Ogni cambio di misura passa da relayout(): legge offsetLeft e offsetTop di tutti i gettoni (la posizione senza le trasformazioni), cambia la larghezza dello spazio, rilegge, e la differenza diventa lo spostamento iniziale di una molla per gettone, integrata a passi piccoli sul ticker di GSAP (STIFF, DAMP, appena sotto lo smorzamento critico: un pixel di troppo). Le molle si sommano, quindi scrivere in fretta non fa mai scatti. Un gettone finito su un’altra riga invece aspetta 0,2 secondi invisibile, mentre la riga gli fa posto, e cade dall’alto di DROP pixel.

Lo spazio che sta avanti alla scrittura

La larghezza dello spazio si misura con un gemello nascosto con lo stesso carattere. Mentre ci scrivi resta ROOM em più lungo del testo e si allunga di un intero ROOM quando la scrittura ci arriva: così le parole dopo si spostano sempre lontano dalle lettere e mai sopra. Lasciato, si richiude intorno alla parola. Le scelte cambiano parola con un rullo (la vecchia sale via, la nuova sale da sotto) e la loro larghezza segue un tween, che relayout trasforma passo per passo nel movimento delle parole dopo.

Gli errori come la fine della frase

All’invio ogni spazio che non va prende il blu, la sua riga si allunga e torna con elastic.out e il suo gettone riceve una spinta sulla sua molla (SHAKE). Accanto al pulsante la frase continua con l’elenco di quello che manca, ognuno un pulsante che porta il fuoco al suo spazio; l’elenco si aggiorna mentre correggi e se ne va quando è tutto a posto. Un indirizzo email sbagliato dice cosa manca: la chiocciola, gli spazi, la fine come .com.

La frase firmata

Inviata (post() finge la richiesta per WAIT secondi), le righe sotto gli spazi si chiudono verso il centro sfalsate, gli spazi si stringono intorno al testo, il peso di Fraunces sale da WRITTEN a SIGNED e la larghezza delle frecce scende a zero, tutto in un tween che a ogni passo rimisura gli spazi dentro relayout: la frase si riassesta da sola. Una riga blu la firma da sinistra a destra; il pulsante cambia parola con un rullo e prende la nuova larghezza con un rimbalzo.

Valori da cambiare

NomeFileDi partenzaCosa cambia
STIFFscript.js520Quanto è rigida la molla di ogni parola: più alta, le parole arrivano prima.
DAMPscript.js34Quanto è frenata la molla: più bassa, le parole vanno più oltre prima di posarsi.
DROPscript.js14Da quanto in alto cade una parola che passa alla riga dopo, in pixel.
ROOMscript.js1.2Quanto lo spazio resta avanti alla scrittura, in em (la larghezza di una M).
SHAKEscript.js90Quanto forte scatta uno spazio che manca all’invio, in pixel al secondo.
WRITTENscript.js400Il peso di quello che si scrive negli spazi.
SIGNEDscript.js600Il peso che prende quello che hai scritto quando la frase è inviata.
WAITscript.js0.8Quanti secondi dura l’invio finto.
SENTscript.jsThanks, {name}: we’ll write back within two days.La riga accanto al pulsante dopo l’invio; {name} è il nome scritto.

Provali nel Playground

Quando usarla

Nella pagina dei contatti di uno studio, di un fotografo, di un artigiano o di un libero professionista: dove una richiesta ha poche domande e il tono conta quanto le risposte. La frase dice a chi scrive cosa serve senza un elenco di caselle, e la frase firmata è la ricevuta di quello che ha chiesto.

Sul telefono

La frase scende a 25 pixel e va a capo su sei righe nei 358 pixel di un telefono di 390, con le stesse molle; il pulsante va sotto la frase e la riga dei messaggi sotto il pulsante. Un tocco su uno spazio apre la tastiera giusta (le email con la chiocciola, il tasto Invio che dice Send nell’ultimo), un tocco su una scelta apre il selettore del telefono. Gli input sono di almeno 16 pixel, quindi Safari non ingrandisce la pagina.

Con il movimento ridotto

Le parole non scivolano e non cadono: la frase va a capo subito, al suo posto. Le scelte cambiano parola in dissolvenza, gli errori prendono il blu senza scatti; all’invio righe e frecce svaniscono, il testo prende peso in un colpo e la riga blu compare in dissolvenza.

Trappole

  • Con lo spazio largo esattamente quanto il testo, a ogni lettera le parole dopo partivano dalla posizione vecchia, cioè sopra la lettera nuova, e per un attimo si leggevano accavallate. Ora lo spazio sta ROOM avanti alla scrittura e si allunga a scatti: le parole si spostano sempre verso il vuoto.
  • Una parola che andava a capo volava in diagonale da una riga all’altra e copriva le vicine, che intanto le facevano posto. Ora aspetta invisibile due decimi di secondo, il tempo che la riga si apra, e poi cade nello spazio libero.
  • Le frecce delle scelte svanivano ma tenevano la loro larghezza, e nella frase firmata restava un buco prima della virgola. Ora la loro larghezza è una variabile CSS che scende a zero nello stesso tween del peso, rimisurata dentro relayout.
  • Le molle a 0,62 di smorzamento andavano oltre dell’8 per cento: su uno spostamento di 80 pixel erano 6 pixel, e la parola toccava il punto prima di lei. A 34 di DAMP vanno oltre di un pixel.

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 11 KB; le librerie: GSAP 28 KB.

Collaudo

Provata il 19 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