Vai al contenuto
animorio
EN
Tutti i componenti

La fisarmonica a interlinea

Fisarmoniche, Testo, TransizioniGSAP, Solo CSSImpegnativaPeso compresso: Demo 8,6 KBGSAP 28 KBGratisScarica lo zip

Le domande frequenti di una piccola stamperia, nello stile minimal: una risposta chiusa non sparisce, ma diventa le sue righe schiacciate in altezza e posate tutte sulla stessa quota, un filetto scuro di due pixel fatto di testo sotto la domanda. Aprendola, l’interlinea cresce dal vivo: le righe si staccano una dopo l’altra, la più lontana per prima, e mentre si separano si gonfiano fino alla loro altezza, si allargano sugli assi del carattere e si posano con un pixel di troppo, finché si leggono.

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 leading accordion</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the questions of a small print studio. A shut answer is all its lines set on top of one another, with no
         leading: one dark rule made of text under the question. Opening it, the leading grows and the lines come
         apart one after another until they can be read. The script cuts each answer into its lines; the paragraph
         stays for screen readers -->
    <div class="faq" data-faq data-autoclic>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q1" aria-expanded="false" aria-controls="faq-a1"><span class="faq__text">What is the smallest run you print?</span><span class="faq__icon" aria-hidden="true"><span class="faq__bar"></span><span class="faq__bar"></span></span></button></h3>
        <div class="faq__a" id="faq-a1" role="region" aria-labelledby="faq-q1"><p class="faq__src">Fifty copies. Below that the plates cost more than the paper, so we print you a single proof first and run the rest once you are happy with it.</p></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q2" aria-expanded="false" aria-controls="faq-a2"><span class="faq__text">Which papers can I choose?</span><span class="faq__icon" aria-hidden="true"><span class="faq__bar"></span><span class="faq__bar"></span></span></button></h3>
        <div class="faq__a" id="faq-a2" role="region" aria-labelledby="faq-q2"><p class="faq__src">Twelve uncoated stocks, from a thin 90 gsm text paper to a 350 gsm card, all from one mill. We post a free sample pack anywhere in Europe.</p></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q3" aria-expanded="false" aria-controls="faq-a3"><span class="faq__text">Can you match a brand colour?</span><span class="faq__icon" aria-hidden="true"><span class="faq__bar"></span><span class="faq__bar"></span></span></button></h3>
        <div class="faq__a" id="faq-a3" role="region" aria-labelledby="faq-q3"><p class="faq__src">Every ink is mixed by hand, so yes. Send us a Pantone reference or a printed swatch and we match it within a shade before the run starts.</p></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q4" aria-expanded="false" aria-controls="faq-a4"><span class="faq__text">How long does an order take?</span><span class="faq__icon" aria-hidden="true"><span class="faq__bar"></span><span class="faq__bar"></span></span></button></h3>
        <div class="faq__a" id="faq-a4" role="region" aria-labelledby="faq-q4"><p class="faq__src">Five to seven working days from the proof you approve. When the press is free a rush job can be ready in three; just ask before you order.</p></div>
      </div>
      <div class="faq__item" data-item>
        <h3 class="faq__head"><button class="faq__q" type="button" id="faq-q5" aria-expanded="false" aria-controls="faq-a5"><span class="faq__text">Do you ship abroad?</span><span class="faq__icon" aria-hidden="true"><span class="faq__bar"></span><span class="faq__bar"></span></span></button></h3>
        <div class="faq__a" id="faq-a5" role="region" aria-labelledby="faq-q5"><p class="faq__src">Yes, flat between two boards so the corners stay sharp. Europe takes two to four days and the rest of the world about a week, always tracked.</p></div>
      </div>
    </div>
  </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.

28 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 «La fisarmonica a interlinea» da Animorio e adattala al mio sito

Cosa fa

Su un fondo bianco cinque domande in Roboto Flex, separate da linee sottili grigie, ognuna con a destra un più fatto di due filetti: What is the smallest run you print?, Which papers can I choose?, Can you match a brand colour?, How long does an order take?, Do you ship abroad? Sotto ogni domanda chiusa c’è la sua risposta, con l’interlinea a zero: tutte le sue righe stanno sulla stessa linea di base, schiacciate in altezza a un decimo, strette sull’asse della larghezza, più grasse sull’asse del grado e con gli spazi fra le parole chiusi. Ne viene un filetto nero di due pixel fatto di testo, pulito, che finisce a destra con un piccolo sfrangiato dove le righe finiscono una dopo l’altra; nessuna lettera si legge. Sotto il puntatore il filetto si apre in tre o quattro filetti più sottili, a quattro pixel l’uno dall’altro, a molla, e si vede che è fatto di righe; la domanda prende un poco di peso. Al clic l’interlinea cresce dal vivo: la riga più lontana parte per prima, poi le altre, ognuna supera il suo posto di un pixel e ci torna, e mentre si separano le righe si gonfiano in altezza quanto lo spazio fra loro permette, così non si coprono mai; intanto si allargano fino alla loro larghezza, il tratto si alleggerisce e le parole riprendono i loro spazi, finché la risposta si legge. La domanda aperta diventa più nera e un poco più stretta, e i due filetti del più girano e si posano uno sull’altro in un meno vermiglio. Aprendo un’altra domanda quella aperta si richiude nello stesso momento: le righe tornano verso il filetto, la più vicina per prima, appiattendosi mentre si avvicinano, ognuna passa di un pixel oltre e torna, e il meno gira ancora nello stesso verso e torna un più. All’apertura le domande arrivano una dopo l’altra, i filetti si stampano riga dopo riga e si apre la prima risposta.

Come funziona

La risposta tagliata in righe

Ogni risposta è un paragrafo vero. Lo script lo compone una volta in un contenitore nascosto largo quanto la colonna, una parola per span, e legge offsetTop di ogni parola: dove cambia, comincia una riga nuova. Ogni riga diventa un div in posizione assoluta, senza andare a capo, mosso solo da un transform. Il paragrafo resta per gli screen reader, nascosto alla vista, e ha l’attributo hidden finché la risposta è chiusa. Se la colonna cambia larghezza, le righe si tagliano di nuovo.

Il filetto fatto di testo

Da chiusa ogni riga sta a y zero, sulla stessa quota delle altre, schiacciata con scaleY a SQUASH (0,11) e con il transform-origin sulla sua linea di base, misurata con uno span vuoto allineato alla base: tutte insieme fanno un filetto di due pixel. Le righe chiuse hanno l’asse wdth a SHUT_WIDTH, l’asse GRAD a SHUT_GRADE (ingrassa il tratto senza allungare le parole), l’asse opsz a SHUT_OPTICAL (in Roboto Flex la larghezza si stringe molto solo ai corpi grandi) e word-spacing a −0,18 em, così il filetto ha meno interruzioni; l’inchiostro è SHUT_INK.

L’interlinea che cresce

Aprendo, ogni riga va da zero al suo posto, i volte l’interlinea (LEADING per il corpo), più OVERSHOOT pixel, con power3.out in OPEN secondi, poi torna al posto con sine.inOut. Parte prima la riga più lontana e le altre STAGGER secondi dopo, così nessuna riga ne attraversa un’altra. Chiudendo succede il contrario, con la più vicina per prima e power3.in, e ogni riga atterra un pixel oltre il filetto e torna.

Alta quanto lo spazio che ha

GSAP muove solo la quota di ogni riga. A ogni fotogramma una funzione dà a ogni riga l’altezza dello spazio fra lei e le vicine, diviso l’interlinea (fra SQUASH e 1,05): così due righe non si coprono mai, nemmeno a metà, e le righe si gonfiano mentre l’interlinea cresce, un capello oltre quando corrono un pixel più in là. Dalla stessa altezza vengono larghezza, grado, corpo ottico, spazi e inchiostro; l’altezza del contenitore sta sotto la riga più bassa. La lista ha un’altezza fissa, tutte chiuse più la risposta più lunga, così la sua cima non si muove mai.

Il più, il puntatore, la tastiera

Il più sono due filetti di 1,5 pixel: aprendo, l’orizzontale gira di 180 gradi e il verticale di 90 con back.out, e si posano uno sull’altro in un meno vermiglio; chiudendo continuano a girare nello stesso verso. Sotto il puntatore, o al primo tocco del dito, il filetto si apre in filetti più sottili, PEEK pixel l’uno dall’altro, con un elastic.out: le righe si allontanano ma restano schiacciate. Ogni domanda è un pulsante in un titolo con aria-expanded e aria-controls; le frecce, Home e End passano da una domanda all’altra.

Valori da cambiare

NomeFileDi partenzaCosa cambia
FIRSTscript.js0La domanda aperta all’apertura della pagina, da 0; −1 per nessuna.
ONLY_ONEscript.jstrueAprendo una domanda si chiude quella aperta.
LEADINGscript.js1.6L’interlinea di una risposta aperta, in volte il corpo del carattere.
OPENscript.js0.72I secondi che una riga ci mette ad arrivare al suo posto (per chiudersi un po’ meno).
STAGGERscript.js0.07I secondi fra una riga che parte e la successiva.
OVERSHOOTscript.js1I pixel di cui una riga supera il suo posto prima di tornarci.
SQUASHscript.js0.11Quanto è schiacciata una riga chiusa, in volte la sua altezza: tutte insieme fanno il filetto.
SHUT_INKscript.js1Quanto è scura ogni riga schiacciata: una sopra l’altra fanno un filetto uniforme.
SHUT_WIDTHscript.js45L’asse della larghezza delle righe chiuse, più strette di quelle aperte (da 25 a 100).
SHUT_OPTICALscript.js32Il corpo ottico delle righe chiuse: Roboto Flex si stringe molto solo ai corpi grandi.
SHUT_GRADEscript.js150L’asse del grado delle righe chiuse: tratti più grassi, stessa larghezza (da 0 a 150).
PEEKscript.js4I pixel di cui le righe schiacciate si separano sotto il puntatore, una dopo l’altra.

Provali nel Playground

Quando usarla

Per le domande frequenti di una stamperia, di una casa editrice, di una rivista, di uno studio o di un negozio: ovunque le risposte sono tante e se ne legge una alla volta, e si vuole che anche chiuse si vedano come testo. Funziona meglio con risposte di due-cinque righe; con una riga sola il filetto è una riga normale un po’ più stretta.

Sul telefono

La lista si stringe allo schermo con 16 pixel di margine per lato; le domande scendono a 18 pixel e le risposte a 15, così vanno su tre o quattro righe. Il primo tocco del dito apre il filetto come il puntatore, e il tocco apre e chiude. Le domande chiuse sono alte 80 pixel.

Con il movimento ridotto

Niente righe che si muovono: la risposta passa dal filetto al testo aperto (e ritorno) già al suo posto, in una breve dissolvenza; il più diventa subito un meno vermiglio e la prima risposta è già aperta. Sotto il puntatore il filetto non si apre.

Trappole

  • La prima versione chiusa teneva le righe sovrapposte a grandezza piena, con un inchiostro leggero che si sommava: nel poster si leggeva come testo rotto, un errore di stampa, e il testo copriva altro testo. Ora le righe chiuse sono schiacciate a un decimo sulla loro linea di base: un filetto pulito, senza lettere che si leggono.
  • Schiacciate e basta, le righe davano un filetto a puntini, interrotto a ogni spazio: con l’inchiostro pieno, gli spazi chiusi a −0,18 em e le righe che coprono una i buchi dell’altra il filetto è quasi uniforme.
  • Gonfiando le righe con un tempo loro, a metà apertura due righe ancora vicine si coprivano: l’altezza ora viene dallo spazio fra una riga e le vicine, quindi non possono coprirsi in nessun istante. Lo spazio dell’hover però non conta, se no le righe sotto il puntatore diventavano testo minuscolo quasi leggibile.
  • Per ingrassare le righe chiuse l’asse wght non va: misurato, a peso 700 una riga si allunga del 6% e sul telefono il filetto uscirebbe dalla colonna. Il tratto si ingrassa con l’asse GRAD, che lascia le parole lunghe uguali al pixel.
  • A 16 pixel l’asse della larghezza di Roboto Flex quasi non lavora (da 100 a 40 la riga si accorciava solo del 10%): con il corpo ottico alzato a 32 per le righe chiuse si stringono di un quarto, e aprendosi si vede che si allargano.
  • Il giro dell’anteprima lasciava le righe separate di qualche pixel: la molla del puntatore durava più dell’apertura e la ritirava su. Aprendo, la molla del puntatore ora si ferma; e con un’interlinea di 25,5 pixel le domande cadevano a mezzo pixel e i più sembravano di spessori diversi, per questo interlinea e altezza della lista sono arrotondate al pixel.

Librerie

GSAP 3.15.0, Roboto Flex (font variabile, con gli assi della larghezza, del grado e del corpo ottico) 5.3.0.

Compressi con gzip, i tre file della demo pesano 8,6 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