Questa lezione ricrea un poster modernista in stile svizzero in Webflow e lo porta alla vita con GSAP — una composizione a tutto viewport dove le lettere “JAM” sono ritagliate da un campo rosso, per poi scorrere dentro e fuori in una sequenza a loop, cinematografica. È uno dei miei tipi di build preferiti: metà è cura silenziosa del layout, l’altra metà è movimento, e le due devono incontrarsi nel mezzo alla perfezione.
Lo affrontiamo nell’ordine in cui è stato costruito. Prima la struttura e la tipografia, dimensionate perché il tutto si comporti come un poster stampato. Poi la parte più insidiosa — ritagliare le lettere con un clip-path CSS modellato in Figma. Poi due interazioni GSAP: un reveal del testo in stagger e una sequenza slide-mask per le lettere grandi. E infine le piccole correzioni che separano una demo da qualcosa che spediresti davvero: una striscia di anti-aliasing, la responsività, e il flicker che ottieni quando GSAP si carica.
Nessuno dei singoli pezzi è difficile. La magia sta in come si sovrappongono.
Come funziona
Un poster che sta nel viewport. La sezione è 100vh con sfondo rosso, e il content wrapper è dimensionato per sembrare stampa: aspect ratio 2:3, flex column, allineato in basso così le lettere grandi stanno in fondo come un vero flyer. La decisione chiave sono le unità — tutto il testo è dimensionato in vh e il letter-spacing in em. È voluto: legando la tipografia all’altezza del viewport, tutto scala insieme sugli schermi più bassi, così la composizione non trabocca mai in verticale. È lo stesso istinto del progettare dentro una cornice di poster fissa.
Ritagliare le lettere. Questo è il cuore del build. Non disegniamo le lettere — le ritagliamo da un blocco rosso pieno usando un clip-path CSS, e un clip-path mostra solo ciò che sta dentro la sua forma. Quindi la forma che ci serve non sono le lettere stesse, è il negativo intorno a esse. Quella forma si costruisce in Figma: scrivi “JM”, converti in vettore, cancella i punti di ancoraggio che formano il puntino sulla J, metti un rettangolo dietro e lancia un boolean subtract così le lettere vengono scavate dal rettangolo. Esporta come SVG. La lettera A si gestisce a parte — è un semplice SVG con fill: currentColor così eredita il colore del genitore, in un suo wrapper così potrà essere animata da sola più tardi.
Normalizzare il path. Un path SVG esportato parla in pixel assoluti. Un clip-path CSS impostato su objectBoundingBox parla in un range 0–1 così da poter scalare con qualunque elemento ritaglia. Fare da ponte tra i due è “normalizzare”: moltiplica ogni coordinata per 1/larghezza e 1/altezza. SVG Path Editor lo fa per te — incolla i dati del path, annota larghezza e altezza dell’SVG, e lancia uno scale con 1/larghezza e 1/altezza. Nel caso del video erano 1/15 ≈ 0.067 e 1/11 ≈ 0.091. Il risultato è un path che si mappa in modo pulito su qualsiasi box.
Due animazioni pulite, un unico trigger al caricamento. Entrambe le interazioni partono al page load. Il reveal del testo targetta i quattro piccoli blocchi di testo tramite un attributo personalizzato (non una classe) e anima opacity 0 → 100% e X -50% → 0, in stagger di 0.1s, per uno slide-and-fade da sinistra. La sequenza delle lettere grandi è tutta un’unica idea ripetuta: anima una sola proprietà, move X, su tre target — il puntino, l’inner wrapper del JM e la A — con offset e tempi leggermente diversi così scorrono dentro da sinistra (e la A da destra, come se si muovesse dentro il gruppo JM), scorrono di nuovo fuori, poi ritornano. Due protezioni overflow: hidden fanno leggere bene il masking: una sul content wrapper (così il JM sovradimensionato può sbordare oltre il bordo), una sull’inner wrapper delle lettere (così la A resta nascosta dentro il JM finché non scorre).
Le correzioni finali. Tre dettagli lo rendono pronto per la produzione. Compare una sottile linea dove due elementi allineati si muovono insieme — un artefatto di anti-aliasing sub-pixel, risolto con un margine di -1px sulle section letters. La responsività è un paio di ritocchi di offset in vh al breakpoint portrait. E il flicker al caricamento — dove gli elementi lampeggiano prima che GSAP imposti il loro stato iniziale — si risolve nascondendo il content wrapper con del CSS nell’head e ripristinandone l’opacity come primissima action dell’interazione.
Come usarlo
-
Costruisci la cornice. Sezione a
100vh, sfondo rosso. Dentro, un main container (height: 100%, flex, centrato). Dentro quello, unsection content wrapper:position: relative, flex column, allineato in basso, aspect ratio2:3,overflow: hidden. Imposta qui la tipografia — peso medio,line-height: 1.3,font-size: 2.5vh,letter-spacing: -0.02em, la maggior parte del testo allineata a destra. -
Struttura le lettere grandi. Dentro il content wrapper aggiungi un letters wrapper che contiene due cose: un div
section dot(width: 13%,aspect-ratio: 1, sfondo chiaro) e unsection letters inner wrapper(position: relative). Dentro l’inner wrapper aggiungisection letters(aspect ratio landscape, sfondo rosso — è ciò che verrà ritagliato) e unsection letter wrapperper la A (position: absolute, full). Riordina nel navigator e impostasection letterssuposition: relativecosì si impila sopra il wrapper della A posizionato in assoluto. -
Crea la A. In Figma scrivi “A” nel font/peso giusti, copia come SVG e incollala in un embed dentro il wrapper della A. Imposta il
filldell’SVG sucurrentColore width/height al100%. -
Crea la forma ritagliata. In Figma scrivi “JM”, converti il testo in vettore (
Cmd/Ctrl+E), cancella i punti di ancoraggio che formano il puntino della J, metti un rettangolo ben aderente dietro le lettere, seleziona entrambi e lancia Object → Boolean → Subtract. Copia il risultato come SVG. -
Normalizza il path. Incolla l’SVG in SVG Path Editor. Tieni solo i dati del path
d(daMaZ), annota width/height, poi usa l’operazione di scale con1/larghezzae1/altezzaper rimappare le coordinate nel range0–1. Copia il path normalizzato. -
Definisci e applica il clip-path. Aggiungi un embed in cima alla pagina (la definizione deve venire prima di qualsiasi cosa la referenzi) contenente un SVG inline con un
clipPathconid="jm"eclipPathUnits="objectBoundingBox", con il tuo path normalizzato nel suod. Dai all’embedposition: absolute. Poi, sull’elementosection letters, aggiungi una custom propertyclip-path: url(#jm). Ora le lettere appaiono ritagliate. -
Rifinisci il layout. Allarga l’inner wrapper delle lettere a ~
127%con un offset a sinistra di ~-8.5%così la M viene tagliata sul bordo destro; l’overflow: hiddendel content wrapper nasconde l’eccesso. Sposta il puntino di ~8.5%per allinearlo alla J. Dai al wrapper della A display flex, ~15%di padding orizzontale e ~4%verticale, e imposta la A stessa a ~73%di altezza. Aggiungi i tuoi due text wrapper (in alto e in basso),position: absolute, con offset di7vh. -
Anima il testo (page load). Aggiungi a ciascuno dei quattro blocchi di testo un attributo personalizzato tipo
data-content-role="text". Crea un’interazione al page load, aggiungi una custom action che targetta quell’attributo (tutti gli elementi corrispondenti), tipo From, animando opacity0%emove X: -50%, durata ~4s, ease power-out, stagger a offset time0.1s. -
Anima le lettere grandi. Nella stessa interazione, costruisci prima il puntino: una action From su
move X: -200%, poi una action To fuori verso unmove Xpositivo grande (es.500%), con start time ed ease corrispondenti. Duplica quella coppia per l’inner wrapper del JM (dentro da-100%, fuori dall’altra parte) e per la A (dentro da destra, fuori a sinistra). Poi duplica di nuovo le tre action “in” come secondo giro impostato su tipo From conmove X: 0%così tutto ritorna per un loop senza cuciture. Aggiungioverflow: hiddensull’inner wrapper delle lettere così la A resta mascherata dentro il JM. -
Rifinitura. Aggiungi un margine di
-1pxsusection lettersper eliminare la striscia di anti-aliasing. Al breakpoint portrait, sistema gli offset dei text wrapper (9vh) e aggiungi del padding in basso (7vh) sul content wrapper. Infine, risolvi il flicker al caricamento: nascondi il content wrapper con una regolaopacity: 0nell’head della pagina, poi fai in modo che la primissima action dell’interazione riporti l’opacity di quel wrapper a100%(durata 0), trascinata in cima alla timeline.