Vai al contenuto
animorio
EN
Tutti i componenti

Il campo che conta le lettere

Moduli, Testo, SVGGSAPImpegnativaPeso compresso: Demo 12 KBGSAP 28 KBGratisScarica lo zip

Nello stile minimal: la bio di un profilo, 160 caratteri, senza riquadro. Sotto il testo una linea sottile è lo spazio che resta: si accorcia a ogni lettera e finisce sul numero dei caratteri rimasti, in cifre tabellari che prendono peso man mano che il limite si avvicina senza mai cambiare larghezza. Oltre il limite le lettere in più sbiadiscono, un filetto arancio le barra una dopo l’altra, un segno dritto mostra dove cade il limite e il numero va sotto zero; cancellandole la linea esce di nuovo dal numero con una molla. Salvando, una linea più scura corre fino al numero e la pillola dice Saved.

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 field that counts its letters</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the short bio of a profile, 160 characters. No box round the field: its underline is the room that is left,
         a hairline that gets shorter with every letter and ends at the count. Past the limit the extra letters are
         struck through with a hairline of the accent and the count goes below zero; deleting them, the line grows
         back on a spring. The marks over the text and the line are drawn by the script -->
    <form class="bio is-waiting" action="#" novalidate data-bio data-autoclic>
      <div class="bio__head">
        <label class="bio__label" for="bio-text" data-label>Bio</label>
      </div>

      <div class="bio__box" data-box>
        <div class="bio__mirror" aria-hidden="true" data-mirror></div>
        <textarea class="bio__input" id="bio-text" name="bio" rows="3" maxlength="400" spellcheck="false" autocomplete="off" placeholder="A line or two about you" aria-describedby="bio-count bio-note" data-input>Type designer in Porto. I draw display faces for small presses and restore old wood type for anyone who still prints by hand.</textarea>
        <svg class="bio__marks" aria-hidden="true" data-marks>
          <path class="bio__wash" data-wash d=""/>
          <path class="bio__strike" data-strike d=""/>
          <path class="bio__limit" data-limit d=""/>
        </svg>
        <span class="bio__caret" aria-hidden="true" data-caret></span>
      </div>

      <div class="bio__gauge" data-gauge>
        <svg class="bio__rule" aria-hidden="true" data-rule>
          <path class="bio__track" data-track d=""/>
          <path class="bio__line" data-line d=""/>
          <path class="bio__ticks" data-ticks d=""/>
          <path class="bio__run" data-run d=""/>
        </svg>
        <p class="bio__count" id="bio-count" data-count><span class="bio__num" aria-hidden="true" data-num>35</span><span class="sr" data-count-said>35 characters left</span></p>
      </div>

      <div class="bio__foot">
        <p class="bio__note" id="bio-note" data-note><span class="note__line">Shown under your name on your profile.</span></p>
        <button class="save" type="submit" data-save>
          <span class="save__bg" aria-hidden="true"></span>
          <span class="save__labels">
            <span class="save__label is-on" data-word="save">Save</span>
            <span class="save__label" aria-hidden="true" data-word="saving">Saving</span>
            <span class="save__label" aria-hidden="true" data-word="saved">Saved</span>
          </span>
        </button>
      </div>
      <span class="sr" aria-live="polite" data-live></span>
    </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.

26 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 campo che conta le lettere» da Animorio e adattala al mio sito

Cosa fa

Su una pagina bianca la parola Bio, piccola e grigia; sotto, la bio di un profilo già scritta su tre righe (Type designer in Porto…), senza riquadro intorno. Sotto il testo corre una riga sottile con una piccola tacca a ogni capo, come una quota del disegno tecnico: è lo spazio che resta, e finisce sul numero 35, i caratteri ancora liberi. A sinistra della riga, quasi bianca, la misura intera già usata. Più in basso Shown under your name on your profile. e la pillola nera Save. Toccando il campo la parola Bio diventa nera e più piena e la riga scurisce. Ogni lettera scritta toglie la sua parte alla riga da sinistra, con una molla calma, e la tacca di sinistra sente il colpo; il numero scende e prende peso, poco all’inizio e molto vicino al limite, ma le sue cifre restano larghe uguali, così la fine della riga non trema. A zero la riga è finita e resta una tacca sola, arancio. Oltre, le lettere in più sbiadiscono e un filetto arancio le barra una dopo l’altra, anche andando a capo; un segno dritto arancio si alza nel punto preciso del testo dove cade il limite, anche a metà di una parola, e il numero diventa negativo, arancio, con un piccolo assestamento. Sotto si legge Too long to save. Trim the struck-out part. e la pillola diventa chiara: premuta, il filetto si ispessisce un attimo e la frase sotto diventa arancio. Cancellando, il filetto si ritira verso il segno, il segno si ripiega e la riga esce di nuovo dal numero con una spinta, va un poco oltre e si assesta. Premendo Save la pillola dice Saving, una riga più scura corre lungo tutta la misura fino al numero, la tacca e il numero sentono l’arrivo, poi la pillola dice Saved e sotto si legge Saved. Your profile shows it now.

Come funziona

Un gemello invisibile che dice dove cade ogni lettera

La textarea è vera e disegna da sé lettere, cursore e selezione. Accanto, nascosto con visibility: hidden, c’è un div gemello con lo stesso carattere, la stessa larghezza, white-space: pre-wrap e overflow-wrap: break-word, che contiene lo stesso testo in un solo nodo. Per ogni carattere oltre il limite un Range sul gemello, con getClientRects, dà il riquadro della lettera nel suo rigo, anche dopo un a capo. I caratteri si contano come li conta l’occhio (codePointAt, così un emoji vale uno), e il limite si trasforma nella posizione giusta dentro il testo. Il gemello dice anche quanto è alto il testo: la textarea prende quell’altezza (niente barra di scorrimento) e il riquadro la segue con GSAP.

Il filetto, il velo e il segno del limite

Sopra la textarea, in un SVG senza viewBox (un’unità è un pixel), tre tracciati riscritti a ogni fotogramma dai riquadri delle lettere: per ogni rigo un rettangolo del colore della pagina con opacità VEIL che sbiadisce le lettere in più, un filetto da 1,5 pixel all’altezza delle minuscole e un segno dritto dove cade il limite (fra due lettere, o a metà di uno spazio). Il filetto non salta da un rigo all’altro: una molla porta il numero delle lettere barrate verso quelle in più, e la lettera a metà è barrata per la parte che le tocca. Gli spazi a fine rigo restano fuori.

Le molle, fatte a mano

I due capi della riga, il peso del numero, il filetto, il segno e le piccole spinte delle tacche sono molle smorzate integrate a mano sul ticker di GSAP: accelerazione uguale a meno omega al quadrato per la distanza, meno due zeta omega per la velocità, col passo limitato a 1/30 di secondo e diviso in passi da 1/240. Quando la riga si accorcia la molla è quasi senza rimbalzo (zeta 0,86); quando si allunga vale GIVE, e quando le lettere in più se ne vanno il capo sinistro riceve una spinta di PUSH pixel al secondo verso sinistra, così la riga esce dal numero, va un poco oltre e si assesta. Il ciclo si addormenta quando tutte le molle sono ferme e nessun tween muove i colori.

Cifre che prendono peso senza allargarsi

Il numero è in Archivo con le cifre tabellari (tnum). Il peso va da LIGHT a HEAVY con una curva alla potenza CURVE della parte usata, così si sente soprattutto vicino al limite. Ma anche le cifre tabellari si allargano col peso: all’arrivo del carattere lo script misura dieci cifre a ogni peso, da 100 a 900 di 50 in 50, e cerca per bisezione la larghezza dell’asse wdth che le rende larghe come al peso più leggero. Durante il movimento wght e wdth cambiano insieme dalla tabella, e la fine della riga si sposta solo quando cambia il numero delle cifre. Sotto zero c’è il vero segno meno (U+2212, scritto con String.fromCharCode).

Il salvataggio e l’anteprima

Save con la bio troppo lunga non salva: la pillola ha aria-disabled, il filetto si ispessisce con una molla e la frase sotto il campo diventa arancio. Con la bio giusta una riga da 2 pixel corre lungo tutta la misura in WAIT secondi; all’arrivo la tacca e il peso del numero ricevono una spinta, la pillola passa da Saving a Saved (tre parole nello stesso posto della griglia, così non cambia larghezza) e la regione aria-live lo dice. Cmd o Ctrl con Invio salva dalla tastiera. Un clic da script sul modulo (detail 0, l’anteprima della libreria) fa un giro intero in una timeline GSAP, senza prendere il fuoco e con un cursore disegnato: una frase aggiunta fino a sforare, una pausa sulle lettere barrate, la parte in più cancellata, Save, Saved e la bio com’era.

Valori da cambiare

NomeFileDi partenzaCosa cambia
LIMITscript.js160Quanti caratteri può avere la bio.
LIGHTscript.js220Il peso del numero quando c’è ancora tanto spazio.
HEAVYscript.js820Il suo peso al limite e oltre.
CURVEscript.js2.6Quanto tardi il numero prende peso: 1 piano piano dall’inizio, 4 solo nell’ultimo tratto.
GIVEscript.js0.45Quanto è elastica la riga quando si riallunga: 0,25 rimbalza, 1 non rimbalza per niente.
PUSHscript.js480La spinta che riceve la riga quando le lettere in più se ne vanno, in pixel al secondo.
VEILscript.js0.6Quanto sbiadiscono le lettere oltre il limite dietro al filetto, da 0 a 1.
WAITscript.js0.9Quanti secondi dura il salvataggio nella demo, mentre corre la riga più scura.
DONEscript.jsSaved. Your profile shows it now.La frase sotto il campo dopo il salvataggio.

Provali nel Playground

Quando usarla

Dove si scrive un testo breve con un limite: la bio di un profilo, la descrizione di un negozio o di un evento, la didascalia di un post, il riassunto per i motori di ricerca, un messaggio per un biglietto regalo. La riga dice quanto spazio resta senza dover leggere il numero, e il filetto mostra esattamente quali lettere stanno fuori, invece di tagliarle senza avvisare come fa maxlength.

Sul telefono

Il campo prende la larghezza dello schermo (358 pixel su un telefono di 390), il testo scende a 18 pixel su righe da 27, il numero a 20 e la pillola a 42. Il riquadro tiene posto per quattro righe, così la pagina non salta quando il testo va a capo; oltre, cresce con una molla. Un tocco apre la tastiera: ogni lettera accorcia la riga come col computer, e il filetto segue le lettere in più anche quando vanno a capo. La pillola si gonfia sotto il dito come col puntatore.

Con il movimento ridotto

Niente molle: la riga, il peso del numero, il filetto e il segno del limite sono già al loro posto a ogni lettera, senza corse né rimbalzi. Cambiano in dissolvenza solo i colori, la riga sotto il campo e le parole della pillola, che si scambiano una dopo l’altra. Il salvataggio aspetta WAIT secondi senza la riga che corre, poi la pillola dice Saved.

Trappole

  • Le cifre tabellari sono larghe uguali fra loro, ma non fra un peso e l’altro: col peso il numero si allargava e la fine della riga tremava a ogni lettera. Ora una tabella misurata all’arrivo del carattere dice, per ogni peso, quanto stringere l’asse wdth perché dieci cifre restino larghe come al peso più leggero.
  • La textarea che cresceva col testo spostava tutto il modulo verso l’alto, perché la pagina lo tiene al centro: alla quarta riga il campo saltava di 15 pixel. Ora il riquadro tiene posto per quattro righe e cresce solo oltre, con una molla.
  • Il cursore disegnato dell’anteprima era arancio come il segno del limite: alla fine del testo barrato sembravano due segni del limite. Cursore vero e disegnato ora sono neri, e l’arancio resta solo a ciò che sta oltre il limite.
  • La frase sotto il campo diceva quante lettere erano di troppo e cambiava a ogni tasto: cancellando in fretta le righe nuove non facevano in tempo ad apparire e restava letto un numero vecchio. Ora la frase è una sola, e il numero lo dice il contatore.
  • Le tre parole della pillola si scambiavano insieme e per un attimo si leggevano Save e Saving una sopra l’altra: ora la nuova entra quando la vecchia è quasi sparita, anche col movimento ridotto.

Librerie

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Collaudo

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