La marquee è uno dei componenti più amati dai designer e uno dei più odiati da chi deve costruirla per bene. Questa lezione ne costruisce una vera da un canvas vuoto — non solo una marquee che scorre, ma un sistema: senza scatti in qualsiasi direzione, riutilizzabile su tutto il sito con un singolo attributo, e abbastanza solido da fidartene in produzione.
Più del risultato, qui conta il ragionamento. Ogni “perché” — perché due copie, perché -100%, dove va l’overflow, perché un attributo — è ciò che trasforma uno snippet copiato in una tecnica che padroneggi.
Come funziona
Due trail group rendono il loop senza scatti. Una singola riga di elementi non può andare in loop pulito — quando esce non c’è nulla dietro, quindi ottieni un gap e un reset brusco. Il fix è duplicare tutto il contenuto in due gruppi identici (ciascuno un div con classe section_marquee-trail) affiancati dentro un section_marquee in flex. Anima i gruppi verso sinistra, e proprio mentre il primo esce del tutto, il secondo è esattamente dove il primo era iniziato — resetti all’istante e si legge come una striscia infinita.
Anima di -100%, non di una larghezza misurata. L’approccio ingenuo anima il container di un valore calcolato (larghezza item × numero). È fragile: si rompe appena gli item hanno larghezze diverse o ne aggiungi uno. Dato che i due trail group hanno larghezza identica, animarli di -100% della propria larghezza porta sempre il secondo gruppo dove il primo era iniziato — a prescindere da quanti item o da quali dimensioni. È la soluzione generale. (Durata ~40s, easing linear così lo scorrimento è perfettamente costante. Imposta il flex sizing dei trail group su don’t shrink or grow così prendono la loro larghezza naturale.)
Overflow sul container, mai sul body. Per nascondere il contenuto fuori schermo, metti overflow: hidden sul main container tramite una combo class (is-marquee) invece che sulla classe base — così la marquee visibile resta legata alla max-width del contenuto, che di solito è la resa migliore, senza toccare il container ovunque. Mettere overflow hidden sul body è un no assoluto.
Un attributo la rende riutilizzabile. Invece di ricostruire l’interazione per ogni marquee, dai a ogni wrapper un attributo data-animate (es. horizontal-marquee) e imposta il target della action su any element → direct child of → attribute. Questo anima i due trail group di qualsiasi marquee che porta l’attributo, indipendentemente dalle classi — così una sola interazione le alimenta tutte (lascia deselezionata la casella “first match only”, dato che i figli sono due).
La direzione è solo asse + segno. Da questa base ogni variante è un piccolo cambio: l’orizzontale anima move X; la verticale anima move Y; la diagonale anima entrambi. Inverti qualsiasi di esse animando da -100% verso 0 invece che da 0 a -100%. Mettere in pausa in hover è un’interazione hover separata (control pause sul mouse-enter, resume sul mouse-leave), e puoi aggiungere un hover CSS sugli item stessi per dare più vita.
Come si usa
-
Struttura. Section → main container (con una max-width) →
section_marquee(display flex). Dentro, avvolgi tutti gli item in un divsection_marquee-trail(flex), poi duplica quel trail così ci sono due gruppi identici. Imposta il flex sizing dei trail group su don’t shrink or grow; aggiungi un piccolo margine destro agli item per la spaziatura. -
Ritaglia. Aggiungi una combo class
is-marqueeal main container conoverflow: hiddene padding orizzontale a zero. -
Interazione principale. Interazione GSAP al page-load. Dai al wrapper
section_marqueedata-animate="horizontal-marquee". Action: target any element → direct child of →data-animate=horizontal-marquee, anima move X a -100%, durata ~40s, linear, repeat: infinite. -
Varianti. Duplica la sezione, cambia il valore dell’attributo (es.
reversed-horizontal-marquee) e il target della action di conseguenza. Invertita = anima move X da-100%a0. Verticale = anima move Y. Diagonale = anima X e Y insieme. Diagonale invertita = ribalta entrambi. -
Pausa in hover. Aggiungi un’interazione hover sulla marquee: mouse-enter → control pause, mouse-leave → control resume.