Sei un recruiter? Clicca qui!
Marquee continuo in WordPress

Marquee continuo in WordPress: creare una barra scorrevole infinita senza plugin

Scritto da Sergio Pinna il

29/04/2026

Introduzione

In alcuni casi, una semplice sezione statica non basta.

Magari vuoi mostrare una serie di parole chiave, servizi, competenze, loghi, tecnologie utilizzate, recensioni brevi o messaggi promozionali in modo più dinamico. Una soluzione molto usata nel web design moderno è il marquee continuo, cioè una fascia orizzontale con elementi che scorrono in loop da destra verso sinistra.

Attenzione però: non sto parlando del vecchio tag HTML <marquee>, ormai superato e poco adatto a un sito moderno. In questo articolo vediamo come creare un marquee fluido, continuo e personalizzabile in WordPress utilizzando uno snippet inserito tramite Code Snippets o un plugin simile.

La logica è semplice: preparo gli elementi nella pagina, poi uno script JavaScript li organizza automaticamente in una traccia animata duplicata. In questo modo l’animazione può scorrere all’infinito senza creare “buchi” visivi.

Perché creare un marquee continuo

Un marquee può essere utile quando vuoi dare movimento a una pagina senza appesantirla troppo.

Per esempio, può essere usato per mostrare:

  • servizi offerti;
  • tecnologie utilizzate;
  • parole chiave legate al tuo lavoro;
  • loghi o nomi di clienti;
  • frasi brevi;
  • claim promozionali;
  • categorie di progetto;
  • elementi decorativi in una landing page.

Nel mio caso, l’obiettivo era creare una fascia animata da inserire in una pagina specifica del sito, senza installare un ulteriore plugin solo per ottenere un effetto grafico relativamente semplice.

La soluzione migliore è stata quindi usare uno snippet personalizzato con tre componenti:

  1. una funzione PHP per caricare il codice solo nella pagina desiderata;
  2. del CSS per gestire layout, animazione e stile;
  3. del JavaScript per costruire automaticamente la struttura del marquee.

Il risultato finale

L’effetto finale è una barra orizzontale continua dove gli elementi scorrono in modo fluido. Ogni elemento viene separato da una stellina decorativa e il movimento si ripete senza interruzioni.

La parte interessante è che non dobbiamo duplicare manualmente gli elementi nella pagina. Lo fa JavaScript.

Noi inseriamo nella pagina solo gli elementi iniziali, per esempio:

<div class="sergio-marquee">
  <span class="sergio-marquee-item">WordPress</span>
  <span class="sergio-marquee-item">UX Design</span>
  <span class="sergio-marquee-item">SEO</span>
  <span class="sergio-marquee-item">Performance</span>
  <span class="sergio-marquee-item">Responsive Design</span>
</div>

Poi lo script prende questi elementi, li inserisce dentro una “traccia” animata e crea una copia identica del gruppo. Questa duplicazione è il trucco che permette di ottenere un movimento continuo.

Lo snippet completo

Questo è lo snippet PHP inserito in WordPress:

function sergio_marquee_continuo_page_2113() {

if ( ! is_page( 2113 ) ) {
return;
}

?>

<style>
.sergio-marquee {
width: 100%;
overflow: hidden;
}

.sergio-marquee-ready {
display: block;
}

.sergio-marquee-track {
display: flex;
width: max-content;
animation: sergioMarqueeScroll var(--sergio-marquee-speed, 35s) linear infinite;
will-change: transform;
box-shadow: inset 0 0 10px rgba(0,0,0,0.25);
background: #1E2946;
}

.sergio-marquee-group {
padding-top: 15px;
padding-bottom: 15px;
display: flex;
align-items: stretch;
gap: var(--sergio-marquee-gap, 75px);
flex-shrink: 0;
padding-right: var(--sergio-marquee-gap, 75px);
}

.sergio-marquee-item {
flex: 0 0 auto;
width: fit-content !important;
max-width: none !important;
position: relative;
overflow: visible;
}

.sergio-marquee-item::after {
content: "★";
position: absolute;
top: 50%;
right: calc(var(--sergio-marquee-gap, 75px) / -2);
transform: translate(50%, -50%);
font-size: 15px;
line-height: 1;
color: currentColor;
pointer-events: none;
}

@keyframes sergioMarqueeScroll {
from {
transform: translateX(0);
}

to {
transform: translateX(-50%);
}
}

@media (max-width: 768px) {
.sergio-marquee {
--sergio-marquee-gap: 16px;
--sergio-marquee-speed: 28s;
}
}

@media (prefers-reduced-motion: reduce) {
.sergio-marquee-track {
animation-duration: 0.01ms;
animation-iteration-count: 1;
}
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
const marquees = document.querySelectorAll('.sergio-marquee');

marquees.forEach(function (marquee) {
if (marquee.classList.contains('sergio-marquee-ready')) {
return;
}

const items = Array.from(marquee.querySelectorAll('.sergio-marquee-item'));

if (!items.length) {
console.warn('Marquee Sergio: nessun elemento .sergio-marquee-item trovato dentro', marquee);
return;
}

const track = document.createElement('div');
track.className = 'sergio-marquee-track';

const group = document.createElement('div');
group.className = 'sergio-marquee-group';

items.forEach(function (item) {
group.appendChild(item);
});

const clone = group.cloneNode(true);
clone.setAttribute('aria-hidden', 'true');

track.appendChild(group);
track.appendChild(clone);

marquee.innerHTML = '';
marquee.appendChild(track);

marquee.classList.add('sergio-marquee-ready');
});
});
</script>

<?php
}
add_action( 'wp_footer', 'sergio_marquee_continuo_page_2113' );

Perché lo snippet viene caricato solo in una pagina

All’inizio della funzione troviamo questa condizione:

if ( ! is_page( 2113 ) ) {
return;
}

Questa riga serve a caricare CSS e JavaScript solo nella pagina con ID 2113.

È una scelta importante, perché evita di inserire codice inutile in tutte le pagine del sito. Se il marquee serve solo in una pagina specifica, non ha senso far caricare lo script anche nella home, nel portfolio, negli articoli del blog o nella pagina contatti.

Questo approccio è molto più pulito rispetto a incollare CSS e JavaScript globalmente ovunque.

Se vuoi usare lo stesso marquee in un’altra pagina, ti basta cambiare l’ID:

if ( ! is_page( 1234 ) ) {
    return;
}

Oppure, se vuoi abilitarlo su più pagine, puoi usare:

if ( ! is_page( array( 2113, 2450, 3001 ) ) ) {
return;
}

La struttura HTML da inserire nella pagina

Per far funzionare lo snippet, nella pagina bisogna inserire un contenitore principale con classe:

sergio-marquee

Al suo interno, ogni elemento da far scorrere deve avere la classe:

sergio-marquee-item

Esempio:

<div class="sergio-marquee">
<span class="sergio-marquee-item">Siti WordPress</span>
<span class="sergio-marquee-item">Web Design</span>
<span class="sergio-marquee-item">SEO tecnica</span>
<span class="sergio-marquee-item">Ottimizzazione performance</span>
<span class="sergio-marquee-item">Responsive design</span>
<span class="sergio-marquee-item">UX Design</span>
</div>

Questa è la struttura minima.

Lo script poi trasforma automaticamente questo markup in una struttura più completa, simile a questa:

<div class="sergio-marquee sergio-marquee-ready">
  <div class="sergio-marquee-track">
    <div class="sergio-marquee-group">
      <!-- elementi originali -->
    </div>
    <div class="sergio-marquee-group" aria-hidden="true">
      <!-- elementi clonati -->
    </div>
  </div>
</div>

La cosa bella è che questa struttura non devi scriverla a mano. Viene generata automaticamente dal JavaScript.

Come funziona il CSS

La classe principale è questa:

.sergio-marquee {
width: 100%;
overflow: hidden;
}

Il contenitore occupa tutta la larghezza disponibile e nasconde tutto ciò che esce dai suoi bordi. Questo è fondamentale: gli elementi scorrono fuori dallo schermo, ma l’utente vede solo la parte interna al contenitore.

Poi troviamo la traccia:

.sergio-marquee-track {
    display: flex;
    width: max-content;
    animation: sergioMarqueeScroll var(--sergio-marquee-speed, 35s) linear infinite;
    will-change: transform;
    box-shadow: inset 0 0 10px rgba(0,0,0,0.25);
    background: #1E2946;
}

Questa è la parte che si muove.

La proprietà più importante è:

animation: sergioMarqueeScroll var(--sergio-marquee-speed, 35s) linear infinite;

Significa che l’animazione si chiama sergioMarqueeScroll, dura di default 35s, ha andamento lineare e si ripete all’infinito.

Il valore della durata non è scritto in modo fisso, ma passa da una variabile CSS:

--sergio-marquee-speed

Se questa variabile non viene definita, viene usato il valore di fallback:

35s

Questo rende lo snippet molto più comodo da personalizzare.

L’animazione vera e propria

L’animazione è questa:

@keyframes sergioMarqueeScroll {
from {
transform: translateX(0);
}

to {
transform: translateX(-50%);
}
}

La traccia parte da translateX(0) e si sposta verso sinistra fino a translateX(-50%).

Perché proprio -50%?

Perché dentro la traccia ci sono due gruppi identici:

  1. il gruppo originale;
  2. il gruppo clonato.

Quando il primo gruppo ha finito di scorrere, il secondo si trova esattamente nella stessa posizione visiva. A quel punto l’animazione riparte da capo, ma l’utente non percepisce lo stacco.

Questo è il vero trucco del marquee continuo.

Se ci fosse un solo gruppo, alla fine dell’animazione si vedrebbe un salto. Con due gruppi identici, invece, il loop appare molto più fluido.

La gestione della distanza tra gli elementi

Gli elementi del marquee vengono distanziati così:

.sergio-marquee-group {
gap: var(--sergio-marquee-gap, 75px);
padding-right: var(--sergio-marquee-gap, 75px);
}

Anche qui viene usata una variabile CSS:

--sergio-marquee-gap

Di default la distanza è 75px.

Questa distanza viene usata sia come gap tra gli elementi, sia come padding-right finale del gruppo. Questo aiuta a mantenere coerente lo spazio tra l’ultimo elemento del primo gruppo e il primo elemento del gruppo duplicato.

La stellina tra gli elementi

Ogni elemento ha una stellina decorativa generata tramite pseudo-elemento CSS:

.sergio-marquee-item::after {
content: "★";
position: absolute;
top: 50%;
right: calc(var(--sergio-marquee-gap, 75px) / -2);
transform: translate(50%, -50%);
font-size: 15px;
line-height: 1;
color: currentColor;
pointer-events: none;
}

La stellina viene inserita automaticamente dopo ogni elemento grazie a:

content: "★";

La cosa interessante è che il colore viene ereditato dal testo dell’elemento:

color: currentColor;

Quindi, se il testo è bianco, anche la stellina sarà bianca. Se il testo cambia colore, la stellina si adatta.

Questo evita di dover gestire un colore separato.

Adattamento su mobile

Su schermi piccoli, lo snippet riduce la distanza tra gli elementi e modifica la velocità:

@media (max-width: 768px) {
.sergio-marquee {
--sergio-marquee-gap: 16px;
--sergio-marquee-speed: 28s;
}
}

Su mobile il gap passa da 75px a 16px.

Questa è una buona scelta perché su smartphone gli spazi troppo ampi rischiano di rendere il marquee poco leggibile: l’utente vedrebbe pochi elementi alla volta e l’effetto risulterebbe troppo “vuoto”.

La velocità invece passa da 35s a 28s, quindi lo scorrimento diventa leggermente più rapido.

Naturalmente questi valori si possono personalizzare.

Per esempio, se vuoi un marquee più lento su mobile, puoi usare:

@media (max-width: 768px) {
    .sergio-marquee {
        --sergio-marquee-gap: 20px;
        --sergio-marquee-speed: 40s;
    }
}

Attenzione all’accessibilità

Un dettaglio importante dello snippet è questo:

@media (prefers-reduced-motion: reduce) {
.sergio-marquee-track {
animation-duration: 0.01ms;
animation-iteration-count: 1;
}
}

Questa media query rispetta le preferenze degli utenti che hanno scelto di ridurre le animazioni nel proprio sistema operativo.

È una piccola attenzione, ma molto importante.

Non tutti gli utenti gradiscono elementi in movimento continuo. Alcune persone possono trovare fastidiose le animazioni permanenti, soprattutto se troppo veloci o troppo invasive.

Con prefers-reduced-motion, il sito si comporta in modo più rispettoso nei confronti dell’utente.

Come funziona il JavaScript

Il JavaScript parte al caricamento della pagina:

document.addEventListener('DOMContentLoaded', function () {

Questo significa che lo script aspetta che il documento HTML sia pronto prima di cercare gli elementi del marquee.

Poi seleziona tutti i contenitori con classe:

.sergio-marquee

Grazie a questa riga:

const marquees = document.querySelectorAll('.sergio-marquee');

Questo è utile perché lo stesso codice può gestire anche più marquee nella stessa pagina.

Il controllo sugli elementi già inizializzati

Subito dopo viene fatto un controllo:

if (marquee.classList.contains('sergio-marquee-ready')) {
return;
}

Questo evita che lo stesso marquee venga inizializzato due volte.

È una protezione intelligente, soprattutto in contesti WordPress dove alcuni script possono essere ricaricati, reinseriti o manipolati da builder, ottimizzatori e plugin di cache.

Il controllo sugli elementi interni

Lo script cerca gli elementi da animare:

const items = Array.from(marquee.querySelectorAll('.sergio-marquee-item'));

Se non trova nessun elemento, mostra un messaggio nella console:

if (!items.length) {
console.warn('Marquee Sergio: nessun elemento .sergio-marquee-item trovato dentro', marquee);
return;
}

Questo è molto utile in fase di debug.

Se il marquee non funziona, una delle prime cose da controllare è proprio questa: gli elementi interni hanno davvero la classe sergio-marquee-item?

Se manca quella classe, lo script non ha nulla da animare.

La creazione automatica della traccia

Lo script crea un nuovo elemento div:

const track = document.createElement('div');
track.className = 'sergio-marquee-track';

Questo div sarà la traccia animata.

Poi crea il gruppo:

const group = document.createElement('div');
group.className = 'sergio-marquee-group';

Dentro questo gruppo vengono spostati gli elementi originali:

items.forEach(function (item) {
group.appendChild(item);
});

Qui è importante notare una cosa: gli elementi non vengono copiati, ma spostati dentro il nuovo gruppo.

Dopo aver creato il gruppo originale, lo script lo clona:

const clone = group.cloneNode(true);
clone.setAttribute('aria-hidden', 'true');

Il clone viene marcato con:

aria-hidden="true"

Questo serve a comunicare ai lettori di schermo che il secondo gruppo è solo una copia decorativa e non deve essere letto due volte.

Altro piccolo dettaglio, ma molto utile.

Perché viene svuotato il contenitore originale

Alla fine troviamo:

marquee.innerHTML = '';
marquee.appendChild(track);

Lo script svuota il contenitore iniziale e inserisce al suo interno la nuova struttura generata.

Questo permette di partire da un HTML semplice e ottenere una struttura più complessa solo quando serve.

Infine viene aggiunta la classe:

marquee.classList.add('sergio-marquee-ready');

Questa classe indica che il marquee è pronto.

Personalizzare velocità e distanza

Uno dei vantaggi di questo snippet è che velocità e spaziatura possono essere personalizzate tramite variabili CSS.

Per esempio:

<div class="sergio-marquee" style="--sergio-marquee-speed: 45s; --sergio-marquee-gap: 90px;">
  <span class="sergio-marquee-item">WordPress</span>
  <span class="sergio-marquee-item">SEO</span>
  <span class="sergio-marquee-item">UX Design</span>
</div>

In questo caso il marquee sarà più lento, perché la durata dell’animazione passa a 45s.

Se invece vuoi renderlo più veloce:

<div class="sergio-marquee" style="--sergio-marquee-speed: 20s;">
  <span class="sergio-marquee-item">WordPress</span>
  <span class="sergio-marquee-item">Performance</span>
  <span class="sergio-marquee-item">Web Design</span>
</div>

Più basso è il numero di secondi, più veloce sarà lo scorrimento.

Più alto è il numero di secondi, più lento sarà il movimento.

Possibile miglioramento: pausa al passaggio del mouse

Una miglioria interessante potrebbe essere fermare il marquee quando l’utente passa sopra con il mouse.

Basta aggiungere questo CSS:

.sergio-marquee:hover .sergio-marquee-track {
    animation-play-state: paused;
}

Questa soluzione può essere utile soprattutto quando nel marquee ci sono contenuti testuali che l’utente potrebbe voler leggere con calma.

Personalmente la userei se il marquee contiene frasi, servizi o testi lunghi. Se invece contiene solo parole brevi o elementi decorativi, la pausa al passaggio del mouse può anche non essere necessaria.

Possibile miglioramento: nascondere la stellina sull’ultimo elemento

Nel codice attuale, la stellina viene generata dopo ogni elemento. Visivamente funziona perché il gruppo è continuo e duplicato.

Se però vuoi avere un controllo più preciso, potresti decidere di non mostrarla sull’ultimo elemento del gruppo:

.sergio-marquee-group .sergio-marquee-item:last-child::after {
    display: none;
}

Nel tuo caso, però, io non la considererei obbligatoria. Siccome il marquee è pensato come loop continuo, la stellina tra la fine del primo gruppo e l’inizio del clone può aiutare a mantenere il ritmo visivo.

Possibile miglioramento: stile del testo

Lo snippet gestisce già il comportamento del marquee, ma puoi completarlo con uno stile più caratterizzato.

Per esempio:

.sergio-marquee-item {
    color: #ffffff;
    font-size: clamp(18px, 3vw, 42px);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

Con clamp() il testo si adatta meglio ai vari schermi.

La proprietà:

white-space: nowrap;

è molto utile perché evita che le singole frasi vadano a capo durante lo scorrimento.

Esempio completo di HTML da usare nella pagina

Ecco un esempio pronto da inserire in una pagina WordPress:

<div class="sergio-marquee" style="--sergio-marquee-speed: 35s; --sergio-marquee-gap: 75px;">
<span class="sergio-marquee-item">Siti WordPress professionali</span>
<span class="sergio-marquee-item">Web Design su misura</span>
<span class="sergio-marquee-item">UX Design</span>
<span class="sergio-marquee-item">SEO tecnica</span>
<span class="sergio-marquee-item">Performance</span>
<span class="sergio-marquee-item">Responsive Design</span>
<span class="sergio-marquee-item">Ottimizzazione siti esistenti</span>
</div>

Se usi Gutenberg, GenerateBlocks o Elementor, il principio è lo stesso: devi assegnare la classe sergio-marquee al contenitore principale e sergio-marquee-item agli elementi interni.

Errori comuni da controllare

Se il marquee non funziona, controlla questi punti.

Il primo errore possibile è aver inserito la classe sergio-marquee ma non le classi sergio-marquee-item sugli elementi interni. In quel caso lo script non trova nulla da animare.

Il secondo errore possibile è aver sbagliato ID pagina nella funzione PHP. Se lo snippet è limitato alla pagina 2113, ma stai testando il marquee su un’altra pagina, il codice non verrà caricato.

Il terzo errore possibile riguarda la cache. Dopo aver modificato lo snippet, conviene sempre svuotare la cache del sito e, se presente, anche quella del browser.

Il quarto errore possibile è legato agli ottimizzatori JavaScript. Alcuni plugin di performance possono rimandare, combinare o ritardare gli script. Se qualcosa non funziona, conviene verificare se il codice viene effettivamente stampato nel footer della pagina.

Perché questa soluzione è migliore di un plugin

Per un effetto del genere, installare un plugin dedicato può essere eccessivo.

Uno snippet personalizzato ha diversi vantaggi:

  • carica solo il codice necessario;
  • può essere limitato a una singola pagina;
  • è più facile da personalizzare;
  • non aggiunge pannelli inutili in amministrazione;
  • non dipende da shortcode proprietari;
  • può essere adattato al design del sito.

Naturalmente un plugin può avere senso se devi creare tanti marquee diversi, con pannello di gestione, impostazioni visuali e integrazione con contenuti dinamici. Ma se ti serve una fascia animata personalizzata, uno snippet è spesso una scelta più pulita.

Quando usare un marquee e quando evitarlo

Il marquee è un elemento grafico efficace, ma va usato con attenzione.

Funziona bene quando ha un ruolo decorativo o di rinforzo visivo. Per esempio, può dare ritmo a una landing page, separare due sezioni, valorizzare una lista di competenze o creare un effetto più editoriale.

Lo eviterei invece per contenuti fondamentali.

Se un messaggio è davvero importante, non dovrebbe dipendere da un testo che scorre. L’utente deve poterlo leggere subito, senza aspettare che passi nel punto giusto.

Quindi il mio consiglio è: usa il marquee come elemento di supporto, non come unico contenitore di informazioni essenziali.

Conclusione

Creare un marquee continuo in WordPress non richiede per forza un plugin.

Con un piccolo snippet PHP, un po’ di CSS e poche righe di JavaScript è possibile ottenere una barra scorrevole fluida, personalizzabile e caricata solo dove serve.

La parte più importante è la duplicazione automatica del gruppo di elementi: grazie a questa tecnica, l’animazione può scorrere da destra verso sinistra senza interruzioni visibili.

È una soluzione leggera, abbastanza elegante e facilmente adattabile a diversi contesti: portfolio, landing page, home page, sezioni servizi o pagine promozionali.

Come sempre, però, il movimento va usato con criterio. Un marquee ben progettato può aggiungere personalità a una pagina. Uno usato male può diventare solo rumore visivo.

Nel dubbio, meglio tenerlo semplice, leggibile e coerente con il resto del design.

Potrebbe interessarti anche...

Perché abbandonare Elementor e passare a GeneratePress e GenerateBlocks

Perché abbandonare Elementor e passare a GeneratePress e GenerateBlocks

04/05/2026

Un confronto pratico tra Elementor e la combinazione GeneratePress + GenerateBlocks, con focus su velocità, pulizia del codice, manutenzione, SEO e controllo del design.

Leggi l'articolo
Flip-box CSS3: creare un’immagine che si gira al passaggio del mouse senza plugin

Flip-box CSS3: creare un’immagine che si gira al passaggio del mouse senza plugin

30/04/2026

In questo articolo vediamo come creare una flip-box CSS3, cioè un’immagine che si gira al passaggio del mouse mostrando un contenuto sul retro. Tutto senza plugin e senza JavaScript.

Leggi l'articolo
Antispam Contact Form 7: bloccare i bot senza CAPTCHA con uno snippet PHP

Antispam Contact Form 7: bloccare i bot senza CAPTCHA con uno snippet PHP

29/04/2026

Scopri come bloccare i bot su Contact Form 7 senza CAPTCHA, usando uno snippet PHP che controlla il tempo di compilazione del modulo e riduce gli invii spam.

Leggi l'articolo

Hai una domanda su questo articolo?

Scrivimi pure nei commenti: ti risponderò il prima possibile con un suggerimento pratico.

...aspetta un attimo!

Hai un’idea per un sito, una pagina da migliorare o un progetto ancora da mettere a fuoco? Raccontamelo: possiamo capire insieme da dove partire.

Parlami della tua idea