
Il pulsante elastico
Pulsanti, Hover, Testo
NuovaGratis
La barra in alto di una piccola fonderia di caratteri, nello stile minimal: due filetti sottili e una riga di voci che riempie sempre la stessa misura, come una riga giustificata. Passando su una voce, quella si allarga e prende peso sull'asse della larghezza del carattere, e le altre si stringono quanto basta perché la riga resti lunga uguale, ognuna sulla sua molla. La pagina in cui si è resta un poco più larga e un segno vermiglio sottile scende sopra di lei dal filetto. Al telefono le voci si aprono in una colonna che tiene la stessa altezza allo stesso modo.
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 justified nav</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the top bar of a small type foundry, Tympan. Its links fill the same measure every time, like a justified line:
the link under the pointer widens and takes weight on the font's width axis, the others narrow just enough for
the line to stay exactly as long. The current page stays a little wider and a thin vermilion tick hangs over
it from the top rule. On a phone the links open in a column that keeps its height the same way -->
<header class="bar" data-bar data-autoclic>
<span class="bar__rule bar__rule--top" aria-hidden="true" data-rule></span>
<span class="bar__rule bar__rule--bottom" aria-hidden="true" data-rule></span>
<a class="brand" href="#" aria-label="Tympan, home" data-brand><span class="brand__word">Tympan</span></a>
<button class="toggle" type="button" aria-expanded="false" aria-controls="tympan-links" data-toggle>
<span class="toggle__label" data-toggle-label>Menu</span>
<svg class="toggle__icon" viewBox="0 0 20 20" aria-hidden="true">
<path class="toggle__line" d="M3 7.5h14" data-line/>
<path class="toggle__line" d="M3 12.5h14" data-line/>
</svg>
</button>
<nav class="nav" id="tympan-links" aria-label="Main" data-nav>
<span class="nav__end nav__end--start" aria-hidden="true" data-end></span>
<span class="nav__end nav__end--end" aria-hidden="true" data-end></span>
<ul class="list" data-list>
<li class="item"><a class="link" href="#" aria-current="page" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Typefaces</span></a></li>
<li class="item"><a class="link" href="#" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Trials</span></a></li>
<li class="item"><a class="link" href="#" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Licensing</span></a></li>
<li class="item"><a class="link" href="#" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Custom</span></a></li>
<li class="item"><a class="link" href="#" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Journal</span></a></li>
<li class="item"><a class="link" href="#" data-link><span class="link__mark" aria-hidden="true"><span class="link__tick" data-tick></span></span><span class="link__word" data-word>Studio</span></a></li>
</ul>
</nav>
</header>
</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.
23 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 «La nav giustificata» da Animorio e adattala al mio sitoRoboto Flex ha l'asse della larghezza da 25 a 151 e quello del peso da 100 a 1000. All'avvio e a ogni cambio di misura lo script dà a tutte le parole, una dopo l'altra, i valori di una griglia (sei larghezze per quattro pesi) e legge ogni volta getBoundingClientRect().width: 24 letture in tutto. Fra un punto e l'altro della griglia la larghezza si ricava con due interpolazioni lineari, e non si misura più niente durante il movimento.
Ogni voce ha una larghezza in più (HOVER_WIDTH sotto il puntatore, CURRENT_WIDTH se è la pagina corrente) e un peso. A ogni fotogramma una bisezione di 28 passi cerca la larghezza di base b per cui la somma delle parole, ognuna a b più la sua aggiunta, è esattamente la misura meno gli spazi (GAP per ogni spazio). La lista è un flex con justify-content: space-between, quindi la prima parola sta all'inizio e l'ultima alla fine: se l'asse arriva in fondo alla corsa, è lo spazio fra le parole a prendersi il resto, come in una riga di tipografia.
Quanto una voce è sotto il puntatore e quanto è la pagina corrente vanno da 0 a 1 su molle smorzate (SPRING), con passi di al massimo 1/120 di secondo e mai più di 1/30 per fotogramma: la parola che si allarga va un poco oltre e torna. Anche la base di ogni parola insegue quella della bisezione con una sua molla, più morbida quanto più è lontana dalla voce che è cambiata (RIPPLE): le vicine si stringono subito, le lontane un attimo dopo, e intanto gli spazi respirano. Se le molle insieme allungassero troppo la riga, una seconda bisezione toglie a tutte la stessa larghezza finché gli spazi non scendono sotto il minimo.
Non conta la parola sotto il puntatore, ma la metà dello spazio: la riga è tagliata a metà di ogni spazio fra due parole. Una parola che si allarga sotto il puntatore si porta dietro i suoi confini, quindi lo tiene finché il puntatore non supera la metà dello spazio dopo: niente avanti e indietro fra due voci quando la riga si sposta. Fuori dalla barra la parola larga torna stretta; il fuoco da tastiera fa lo stesso del puntatore.
Ogni voce ha un segno vermiglio largo 2 pixel appeso al filetto di sopra, al centro della parola: un elemento per la posizione (left: 50%, del CSS) e il suo figlio per GSAP, che lo fa scendere con scaleY e un back.out (un pixel di troppo e il ritorno) e lo ritira con un power2.in. Il segno sta dentro la voce, quindi segue la parola mentre la riga si giustifica. La voce scelta prende aria-current="page".
Sotto i 760 pixel le stesse voci finiscono in un pannello sotto la barra, che si apre col pulsante Menu con un clip-path che scende. L'altezza del pannello non cambia mai (sei righe da 46): la riga sotto il dito si alza di ROW_GROW pixel e le altre cedono lo spazio, la pagina corrente ne tiene un terzo; il carattere della riga cresce con lei, si allarga e prende peso, e se non ci sta prima si stringe e poi rimpicciolisce. Il dito che scorre giù per la colonna sposta la riga grande; alzandolo si sceglie quella voce e il pannello si chiude. Il segno è lo stesso, girato: esce dal bordo sinistro della riga.
| Nome | File | Di partenza | Cosa cambia |
|---|---|---|---|
HOVER_WIDTH | script.js | 56 | Quanto si allarga la voce sotto il puntatore, in unità dell'asse della larghezza (che va da 25 a 151). |
HOVER_WEIGHT | script.js | 720 | Il peso della voce sotto il puntatore (da 100 a 1000). |
CURRENT_WIDTH | script.js | 10 | Quanto resta più larga la pagina in cui si è, sull'asse della larghezza. |
CURRENT_WEIGHT | script.js | 540 | Il peso della pagina in cui si è. |
REST_WEIGHT | script.js | 380 | Il peso delle altre voci. |
GAP | script.js | 38 | Lo spazio che le parole tengono fra loro, in pixel: il resto lo prende l'asse della larghezza. Più grande, voci più strette. |
SPRING | script.js | [230, 22] | La molla di ogni voce: quanto è rigida e quanto è frenata. |
RIPPLE | script.js | 0.35 | Quanto rispondono dopo le voci più lontane da quella che cambia (0, tutte insieme). |
ROW_GROW | script.js | 28 | Al telefono, di quanti pixel si alza la riga sotto il dito (le altre cedono lo spazio). |
Per la testata di chi ha a che fare coi caratteri e con le parole: una fonderia di caratteri, uno studio di grafica, una rivista, una casa editrice, il portfolio di un tipografo o di un designer. Sta bene con cinque, sei o sette voci corte su una barra chiara e vuota; con voci molto lunghe l'asse arriva presto in fondo e lavora solo lo spazio. Colori e pesi si cambiano nelle variabili.
Sotto i 760 pixel la barra è alta 56 pixel e larga quanto lo schermo meno 32 (358 a 390): a sinistra il nome, a destra il pulsante Menu con due linee sottili. Toccato, le linee diventano una croce, la scritta dice Close e sotto la barra scende un pannello bianco con le sei voci in colonna, separate da linee sottili: le parole entrano una dopo l'altra, da strette fino alla loro larghezza, e il segno vermiglio esce dal bordo sinistro della pagina corrente. Il pannello è sempre alto uguale: col dito su una riga quella si alza, la parola diventa grande, larga e piena, e le altre righe si stringono per farle posto; facendo scorrere il dito su e giù la riga grande lo segue. Alzando il dito si sceglie quella voce, il segno scende sulla nuova e il pannello si chiude. Esc chiude e riporta il fuoco sul pulsante.
Niente molle e niente parole che si allargano: la voce sotto il puntatore diventa solo nera, la pagina corrente resta già un poco più larga e la riga è già giustificata. Scegliendo un'altra voce la riga si rifà al suo posto in una breve dissolvenza e il segno vermiglio sfuma sulla vecchia e ricompare sulla nuova, senza scendere. All'apertura tutto compare in dissolvenza; al telefono il pannello compare e sparisce con una dissolvenza, senza scendere, e le righe restano alte uguali.
GSAP 3.15.0, Roboto Flex (font variabile, con gli assi della larghezza e del peso) 5.3.0.
Compressi con gzip, i tre file della demo pesano 9,8 KB; le librerie: GSAP 28 KB.
Provata il 16 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.