Sei un recruiter? Clicca qui!
Come creare un sommario apribile nei singoli articoli WordPress senza plugin

Come creare un sommario apribile nei singoli articoli WordPress senza plugin

Scritto da Sergio Pinna il

22/06/2026

Quando un articolo inizia a diventare lungo, pieno di sezioni, sottosezioni, esempi pratici e blocchi di codice, il rischio è sempre lo stesso: l’utente arriva sulla pagina, vede tanto contenuto e non capisce subito dove andare.

Un buon sommario serve proprio a questo.

Non è solo un elemento estetico. È una piccola mappa dell’articolo. Aiuta il lettore a orientarsi, gli permette di saltare velocemente alla sezione che gli interessa e rende la pagina più ordinata, soprattutto quando si pubblicano guide tecniche, tutorial WordPress, articoli CSS o contenuti molto approfonditi.

In questo articolo vediamo come creare un sommario personalizzato per i singoli articoli WordPress, senza installare plugin aggiuntivi, utilizzando un semplice snippet PHP, un po’ di CSS e una piccola parte JavaScript per gestire l’apertura e la chiusura del box.

L’obiettivo è creare un sommario elegante, responsive e richiudibile con un click.

Perché inserire un sommario negli articoli del blog

Un sommario all’inizio dell’articolo migliora parecchio l’esperienza di lettura, soprattutto nei contenuti lunghi.

Quando un utente arriva su una guida tecnica, spesso non legge tutto dall’inizio alla fine. Magari cerca una parte precisa: il codice CSS, lo snippet PHP, la spiegazione di un passaggio, la sezione dedicata alla personalizzazione o la conclusione.

Senza un sommario, deve scorrere manualmente tutta la pagina.

Con un sommario, invece, può vedere subito la struttura del contenuto e raggiungere rapidamente il punto che gli interessa.

Dal punto di vista pratico, un sommario può aiutare in diversi modi:

  • rende l’articolo più leggibile;
  • migliora la navigazione interna della pagina;
  • dà subito un’idea della struttura del contenuto;
  • è molto utile negli articoli lunghi;
  • migliora l’esperienza utente da desktop e da mobile;
  • rende il blog più ordinato e professionale.

Per un blog tecnico, dove si parla di WordPress, CSS, PHP, GeneratePress, GenerateBlocks o sviluppo web, è un elemento quasi indispensabile.

L’idea del sommario

Il sommario che andiamo a creare è composto da tre parti:

  1. un contenitore principale;
  2. un pulsante per aprire e chiudere il sommario;
  3. una lista di link interni che portano alle sezioni dell’articolo.

Il box del sommario viene mostrato solo nei singoli articoli del blog, quindi non viene caricato su pagine, homepage, archivi, categorie o altre sezioni del sito.

Questa è una scelta importante.

Quando si lavora su WordPress, soprattutto se si usa Code Snippets, è sempre meglio evitare di caricare codice dove non serve. In questo caso, il CSS e il JavaScript del sommario sono utili solo nei post singoli, quindi li facciamo apparire solo lì.

Per farlo useremo questa condizione:

if (!is_singular('post')) {
    return;
}

In pratica stiamo dicendo a WordPress:

“Se non mi trovo dentro un singolo articolo del blog, non caricare nulla.”

Questo rende lo snippet più pulito e più leggero.

La struttura HTML del sommario

Prima di vedere lo snippet completo, immaginiamo la struttura HTML del sommario.

Un esempio semplice potrebbe essere questo:

<div class="sergio-toc">
    <button class="sergio-toc-toggle" aria-expanded="true">
        <span>Sommario dell'articolo</span>
        <span class="sergio-toc-icon">[ Nascondi ]</span>
    </button>

    <div class="sergio-toc-content">
        <ol>
            <li><a href="#perche-inserire-un-sommario">Perché inserire un sommario</a></li>
            <li><a href="#struttura-html-del-sommario">La struttura HTML del sommario</a></li>
            <li><a href="#codice-css">Il codice CSS</a></li>
            <li><a href="#codice-javascript">Il codice JavaScript</a></li>
            <li><a href="#conclusione">Conclusione</a></li>
        </ol>
    </div>
</div>

Il contenitore principale ha classe:

.sergio-toc

Il pulsante che apre e chiude il sommario ha classe:

.sergio-toc-toggle

Il contenuto richiudibile ha classe:

.sergio-toc-content

Infine, l’elemento che cambia testo da [ Nascondi ] a [ Mostra ] ha classe:

.sergio-toc-icon

Questa struttura è molto semplice, ma permette di ottenere un comportamento completo: il sommario si vede, si può chiudere, si può riaprire e i link interni portano alle varie sezioni dell’articolo.

Dove inserire il codice in WordPress

Il metodo più comodo è usare il plugin Code Snippets.

In questo modo non serve modificare direttamente il file functions.php del tema, evitando problemi in caso di aggiornamenti o errori.

Una volta installato Code Snippets, basta creare un nuovo snippet e incollare il codice PHP completo.

Il codice farà due cose:

  • inserirà il CSS nella <head> del sito;
  • inserirà il JavaScript prima della chiusura del body.

La cosa importante è che entrambi verranno caricati solo nei singoli articoli WordPress.

Il codice CSS del sommario

La prima parte dello snippet serve a stampare il CSS nella head del sito.

Ecco il codice:

/* Sommario articoli - CSS nella head */
add_action('wp_head', 'sergio_toc_styles');

function sergio_toc_styles() {

if (!is_singular('post')) {
return;
}

?>
<style>
/* Sommario articolo */
.sergio-toc {
max-height: 460px;
margin: 0 0 40px;
padding: 22px;
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 20px;
background: #1E2946;
overflow-y: auto;
}

.sergio-toc-toggle {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 0;
border: 0;
background: transparent;
color: #fff;
font-family: 'Michroma', 'Arial', sans-serif;
font-size: 1.1rem;
text-align: left;
cursor: pointer;
}

.sergio-toc-toggle:hover,
.sergio-toc-toggle:focus,
.sergio-toc-toggle:active {
background: transparent;
color: #fff;
outline: none;
}

.sergio-toc-icon {
width: auto;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #1e2940;
color: #fff;
font-size: 20px;
line-height: 1;
white-space: nowrap;
}

.sergio-toc-content {
margin-top: 18px;
max-height: 2000px;
opacity: 1;
overflow: hidden;
transform: translateY(0);
transition:
max-height 0.5s ease,
opacity 0.3s ease,
margin-top 0.4s ease,
transform 0.35s ease;
}

.sergio-toc-content ol {
margin: 0;
padding-left: 35px;
}

.sergio-toc-content li {
margin-bottom: 8px;
}

.sergio-toc-content a:hover {
text-decoration: none;
}

.sergio-toc-level-3 {
margin-left: 18px;
font-size: 0.95rem;
}

.sergio-toc.is-closed .sergio-toc-content {
max-height: 0;
opacity: 0;
margin-top: 0;
transform: translateY(-6px);
}

/* Scroll morbido quando clicchi sul sommario */
html {
scroll-behavior: smooth;
}

/* Spazio sopra i titoli quando arrivi con ancora */
.single-post .entry-content h2,
.single-post .entry-content h3 {
scroll-margin-top: 100px;
}

@media (max-width: 768px) {
.sergio-toc {
padding: 18px;
margin-bottom: 32px;
}

.sergio-toc-toggle {
font-size: 1rem;
}

.sergio-toc-icon {
font-size: 16px;
}
}
</style>
<?php
}

Analisi del CSS

La classe principale è questa:

.sergio-toc

Qui viene definito l’aspetto del box del sommario.

.sergio-toc {
max-height: 460px;
margin: 0 0 40px;
padding: 22px;
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 20px;
background: #1E2946;
overflow-y: auto;
}

Il sommario ha uno sfondo blu scuro, un bordo leggero, angoli arrotondati e uno spazio interno abbastanza generoso.

La proprietà:

max-height: 460px;

serve a evitare che il sommario diventi troppo alto nel caso in cui l’articolo abbia tanti titoli.

In quel caso entra in gioco:

overflow-y: auto;

cioè il box può diventare scrollabile internamente.

Questa è una scelta utile soprattutto nei tutorial molto lunghi, dove magari ci sono molti h2 e h3.

Il pulsante di apertura e chiusura

Il pulsante viene gestito da questa classe:

.sergio-toc-toggle

Il CSS lo rende largo quanto tutto il box, con il titolo del sommario a sinistra e il comando [ Nascondi ] o [ Mostra ] a destra.

.sergio-toc-toggle {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 0;
border: 0;
background: transparent;
color: #fff;
font-family: 'Michroma', 'Arial', sans-serif;
font-size: 1.1rem;
text-align: left;
cursor: pointer;
}

Il display: flex è molto comodo perché permette di distribuire bene gli elementi interni.

Il testo del bottone rimane allineato a sinistra, mentre l’icona testuale [ Nascondi ] rimane a destra.

In questo caso non ho usato una vera icona grafica, ma un semplice testo. È una soluzione leggera, leggibile e facile da modificare.

L’effetto morbido di apertura e chiusura

La parte più interessante è questa:

.sergio-toc-content {
margin-top: 18px;
max-height: 2000px;
opacity: 1;
overflow: hidden;
transform: translateY(0);
transition:
max-height 0.5s ease,
opacity 0.3s ease,
margin-top 0.4s ease,
transform 0.35s ease;
}

Qui prepariamo il contenuto del sommario ad aprirsi e chiudersi con un effetto più morbido.

Quando il sommario è aperto, il contenuto ha:

max-height: 2000px;
opacity: 1;
transform: translateY(0);

Quando invece il sommario viene chiuso, viene aggiunta la classe is-closed al contenitore principale.

A quel punto entra in gioco questa regola:

.sergio-toc.is-closed .sergio-toc-content {
max-height: 0;
opacity: 0;
margin-top: 0;
transform: translateY(-6px);
}

Questa regola nasconde il contenuto in modo graduale.

Il risultato è più elegante rispetto a una chiusura secca, perché il sommario non sparisce di colpo, ma si richiude con una piccola transizione.

Lo scroll morbido verso le sezioni

Quando si clicca su un link del sommario, il browser porta l’utente al titolo corrispondente.

Per rendere questo movimento più piacevole, usiamo:

html {
scroll-behavior: smooth;
}

In questo modo lo spostamento non avviene a scatto, ma con uno scroll fluido.

È una piccola cosa, ma migliora molto la percezione della pagina.

Lo spazio sopra i titoli

Un problema classico dei link ad ancora è che, quando si arriva a una sezione, il titolo può finire troppo attaccato alla parte alta dello schermo.

Se il sito ha un header fisso o semplicemente si vuole lasciare un po’ di respiro, conviene aggiungere:

.single-post .entry-content h2,
.single-post .entry-content h3 {
scroll-margin-top: 100px;
}

Questa regola dice al browser di lasciare uno spazio superiore quando raggiunge un titolo tramite ancora.

Il valore 100px può essere modificato in base al layout del sito.

Se hai un header molto alto, puoi aumentarlo. Se invece hai una testata più compatta, puoi ridurlo.

La parte responsive

Il sommario deve funzionare bene anche su mobile.

Per questo è presente una media query:

@media (max-width: 768px) {
.sergio-toc {
padding: 18px;
margin-bottom: 32px;
}

.sergio-toc-toggle {
font-size: 1rem;
}

.sergio-toc-icon {
font-size: 16px;
}
}

Su schermi più piccoli riduciamo leggermente il padding e la dimensione del testo, così il box rimane più compatto.

È importante perché su mobile lo spazio verticale è prezioso. Un sommario troppo grande rischia di occupare metà schermata e diventare fastidioso.

Il codice JavaScript per aprire e chiudere il sommario

La seconda parte dello snippet serve a gestire il click sul pulsante.

Il codice viene inserito nel footer del sito tramite wp_footer.

/* Sommario articoli - apertura / chiusura */
add_action('wp_footer', 'sergio_toc_toggle_script');

function sergio_toc_toggle_script() {

    if (!is_singular('post')) {
        return;
    }

    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
        const toc = document.querySelector('.sergio-toc');
        if (!toc) return;

        const button = toc.querySelector('.sergio-toc-toggle');
        const content = toc.querySelector('.sergio-toc-content');
        const icon = toc.querySelector('.sergio-toc-icon');

        if (!button || !content || !icon) return;

        button.addEventListener('click', function () {
            const isOpen = !toc.classList.toggle('is-closed');

            button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            icon.textContent = isOpen ? '[ Nascondi ]' : '[ Mostra ]';
        });
    });
    </script>
    <?php
}

Come funziona il JavaScript

Il codice parte solo quando il DOM è stato caricato:

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

Questo evita di cercare elementi HTML quando ancora non sono disponibili nella pagina.

Poi selezioniamo il sommario:

const toc = document.querySelector('.sergio-toc');
if (!toc) return;

Se il sommario non esiste nella pagina, lo script si ferma.

Questa è una piccola protezione utile: evita errori JavaScript nelle pagine dove magari il codice è caricato ma il sommario non è presente.

Poi selezioniamo i tre elementi principali:

const button = toc.querySelector('.sergio-toc-toggle');
const content = toc.querySelector('.sergio-toc-content');
const icon = toc.querySelector('.sergio-toc-icon');

Anche qui viene fatto un controllo:

if (!button || !content || !icon) return;

Se manca uno di questi elementi, lo script non va avanti.

Questo rende il codice più robusto.

La classe is-closed

La vera logica di apertura e chiusura è questa:

const isOpen = !toc.classList.toggle('is-closed');

Quando l’utente clicca sul pulsante, JavaScript aggiunge o rimuove la classe:

is-closed

Se la classe viene aggiunta, il sommario si chiude.

Se la classe viene rimossa, il sommario si apre.

Subito dopo aggiorniamo anche l’attributo aria-expanded:

button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');

Questo è utile per l’accessibilità, perché comunica ai lettori di schermo se il contenuto è aperto o chiuso.

Infine aggiorniamo il testo del comando:

icon.textContent = isOpen ? '[ Nascondi ]' : '[ Mostra ]';

Quando il sommario è aperto, viene mostrato [ Nascondi ].

Quando il sommario è chiuso, viene mostrato [ Mostra ].

Questa divisione è sensata.

Il CSS viene caricato nella head perché serve subito al browser per disegnare correttamente il sommario.

Il JavaScript invece viene caricato nel footer, perché serve solo dopo che la pagina è stata costruita.

In WordPress possiamo fare questa cosa usando due hook diversi:

add_action('wp_head', 'sergio_toc_styles');

e:

add_action('wp_footer', 'sergio_toc_toggle_script');

È un approccio molto pulito e ordinato.

Inoltre, grazie alla condizione:

if (!is_singular('post')) {
return;
}

il codice non viene caricato in tutto il sito, ma solo nei singoli articoli.

Codice completo dello snippet

Qui trovi lo snippet completo, pronto da inserire in Code Snippets.

/* Sommario articoli - CSS nella head */
add_action('wp_head', 'sergio_toc_styles');

function sergio_toc_styles() {

    if (!is_singular('post')) {
        return;
    }

    ?>
    <style>
    /* Sommario articolo */
    .sergio-toc {
        max-height: 460px;
        margin: 0 0 40px;
        padding: 22px;
        border: 1px solid rgba(255, 255, 255, 0.25);
        border-radius: 20px;
        background: #1E2946;
        overflow-y: auto;
    }

    .sergio-toc-toggle {
        width: 100%;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 16px;
        padding: 0;
        border: 0;
        background: transparent;
        color: #fff;
        font-family: 'Michroma', 'Arial', sans-serif;
        font-size: 1.1rem;
        text-align: left;
        cursor: pointer;
    }

    .sergio-toc-toggle:hover,
    .sergio-toc-toggle:focus,
    .sergio-toc-toggle:active {
        background: transparent;
        color: #fff;
        outline: none;
    }

    .sergio-toc-icon {
        width: auto;
        height: 28px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        background: #1e2940;
        color: #fff;
        font-size: 20px;
        line-height: 1;
        white-space: nowrap;
    }

    .sergio-toc-content {
        margin-top: 18px;
        max-height: 2000px;
        opacity: 1;
        overflow: hidden;
        transform: translateY(0);
        transition:
            max-height 0.5s ease,
            opacity 0.3s ease,
            margin-top 0.4s ease,
            transform 0.35s ease;
    }

    .sergio-toc-content ol {
        margin: 0;
        padding-left: 35px;
    }

    .sergio-toc-content li {
        margin-bottom: 8px;
    }

    .sergio-toc-content a:hover {
        text-decoration: none;
    }

    .sergio-toc-level-3 {
        margin-left: 18px;
        font-size: 0.95rem;
    }

    .sergio-toc.is-closed .sergio-toc-content {
        max-height: 0;
        opacity: 0;
        margin-top: 0;
        transform: translateY(-6px);
    }

    /* Scroll morbido quando clicchi sul sommario */
    html {
        scroll-behavior: smooth;
    }

    /* Spazio sopra i titoli quando arrivi con ancora */
    .single-post .entry-content h2,
    .single-post .entry-content h3 {
        scroll-margin-top: 100px;
    }

    @media (max-width: 768px) {
        .sergio-toc {
            padding: 18px;
            margin-bottom: 32px;
        }

        .sergio-toc-toggle {
            font-size: 1rem;
        }

        .sergio-toc-icon {
            font-size: 16px;
        }
    }
    </style>
    <?php
}


/* Sommario articoli - apertura / chiusura */
add_action('wp_footer', 'sergio_toc_toggle_script');

function sergio_toc_toggle_script() {

    if (!is_singular('post')) {
        return;
    }

    ?>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
        const toc = document.querySelector('.sergio-toc');
        if (!toc) return;

        const button = toc.querySelector('.sergio-toc-toggle');
        const content = toc.querySelector('.sergio-toc-content');
        const icon = toc.querySelector('.sergio-toc-icon');

        if (!button || !content || !icon) return;

        button.addEventListener('click', function () {
            const isOpen = !toc.classList.toggle('is-closed');

            button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
            icon.textContent = isOpen ? '[ Nascondi ]' : '[ Mostra ]';
        });
    });
    </script>
    <?php
}

Per far funzionare i link interni, ogni titolo dell’articolo deve avere un ID.

Per esempio, se nel sommario hai questo link:

<a href="#codice-css">Il codice CSS</a>

nell’articolo dovrai avere un titolo con lo stesso ID:

<h2 id="codice-css">Il codice CSS</h2>

Il valore dopo il cancelletto nel link deve corrispondere all’ID del titolo.

Quindi:

href="#codice-css"

deve puntare a:

id="codice-css"

In Gutenberg puoi assegnare un’ancora HTML direttamente dalle impostazioni avanzate del blocco titolo.

Basta selezionare il titolo, aprire le impostazioni del blocco e inserire il valore nel campo “Ancora HTML”.

Per esempio:

codice-css

Senza cancelletto.

Il cancelletto si usa solo nel link.

Esempio completo di sommario dentro un articolo

Ecco un esempio pratico di sommario da inserire all’inizio di un articolo tramite blocco HTML personalizzato di Gutenberg:

<div class="sergio-toc">
    <button class="sergio-toc-toggle" aria-expanded="true">
        <span>Sommario dell'articolo</span>
        <span class="sergio-toc-icon">[ Nascondi ]</span>
    </button>

    <div class="sergio-toc-content">
        <ol>
            <li><a href="#introduzione">Introduzione</a></li>
            <li><a href="#perche-usare-un-sommario">Perché usare un sommario</a></li>
            <li><a href="#codice-css">Il codice CSS</a></li>
            <li><a href="#codice-javascript">Il codice JavaScript</a></li>
            <li><a href="#personalizzazioni">Personalizzazioni utili</a></li>
            <li><a href="#conclusione">Conclusione</a></li>
        </ol>
    </div>
</div>

Poi, nel contenuto dell’articolo, i titoli dovranno avere gli ID corrispondenti:

<h2 id="introduzione">Introduzione</h2>

<h2 id="perche-usare-un-sommario">Perché usare un sommario</h2>

<h2 id="codice-css">Il codice CSS</h2>

<h2 id="codice-javascript">Il codice JavaScript</h2>

<h2 id="personalizzazioni">Personalizzazioni utili</h2>

<h2 id="conclusione">Conclusione</h2>

In questo modo, cliccando su una voce del sommario, l’utente verrà portato direttamente alla sezione corrispondente.

Come gestire i sottotitoli H3

Nel CSS è presente anche questa classe:

.sergio-toc-level-3 {
margin-left: 18px;
font-size: 0.95rem;
}

Questa classe può essere usata per differenziare visivamente i titoli di terzo livello, cioè gli h3.

Per esempio:

<ol>
<li><a href="#codice-css">Il codice CSS</a></li>
<li class="sergio-toc-level-3"><a href="#stile-del-box">Stile del box</a></li>
<li class="sergio-toc-level-3"><a href="#effetto-apertura">Effetto apertura</a></li>
<li><a href="#codice-javascript">Il codice JavaScript</a></li>
</ol>

In questo modo i sottotitoli risultano leggermente rientrati e più piccoli.

È un dettaglio utile perché permette all’utente di capire subito la gerarchia del contenuto.

Possibili personalizzazioni

Una volta creata la base, puoi personalizzare facilmente il sommario.

Per cambiare il colore di sfondo, basta modificare:

background: #1E2946;

Per cambiare l’arrotondamento degli angoli:

border-radius: 20px;

Per rendere il sommario più compatto:

padding: 18px;

Per aumentare o diminuire l’altezza massima:

max-height: 460px;

Per modificare la velocità dell’animazione:

transition:
max-height 0.5s ease,
opacity 0.3s ease,
margin-top 0.4s ease,
transform 0.35s ease;

Se vuoi una chiusura più lenta, puoi aumentare i valori, ad esempio da 0.5s a 0.7s.

Se invece vuoi un effetto più rapido, puoi ridurli.

Attenzione alla lunghezza del sommario

Un sommario è utile, ma non deve diventare più pesante dell’articolo stesso.

Se dentro un post ci sono troppi titoli, conviene inserire nel sommario solo le sezioni principali, magari limitandosi agli h2.

Gli h3 sono utili quando l’articolo è molto tecnico, ma vanno usati con un po’ di criterio.

Un sommario troppo lungo rischia di diventare scomodo, soprattutto su smartphone.

Per questo nel CSS abbiamo previsto:

max-height: 460px;
overflow-y: auto;

Così, anche se il sommario contiene molte voci, il box non invade tutta la pagina.

Perché non usare un plugin?

Esistono diversi plugin per creare automaticamente un sommario in WordPress.

Alcuni sono ottimi, ma non sempre servono.

Se hai bisogno di una soluzione semplice, controllata e leggera, uno snippet personalizzato può essere più che sufficiente.

I vantaggi sono abbastanza chiari:

  • non aggiungi un plugin in più;
  • controlli completamente HTML, CSS e JavaScript;
  • carichi il codice solo dove serve;
  • puoi adattare lo stile al design del sito;
  • eviti opzioni inutili;
  • mantieni il sito più pulito.

Naturalmente, questa soluzione richiede un minimo di gestione manuale, soprattutto per quanto riguarda i link e le ancore dei titoli.

Ma per un blog curato, dove ogni articolo viene costruito con attenzione, può essere una scelta molto valida.

Generare automaticamente il sommario leggendo i titoli dell’articolo

Finora abbiamo visto come potrebbe essere costruito manualmente un sommario, ma in un blog WordPress questa soluzione può diventare un po’ scomoda.

Ogni volta bisognerebbe scrivere a mano la lista dei link, controllare gli ID dei titoli, aggiornare il sommario se si aggiunge una nuova sezione e fare attenzione a non dimenticare qualche voce.

Per rendere il sistema più pratico, possiamo fare in modo che WordPress legga automaticamente i titoli presenti nel contenuto dell’articolo e costruisca il sommario da solo.

L’idea è semplice: lo snippet analizza il contenuto del post, intercetta tutti i titoli h2 e h3, crea una lista ordinata di link e inserisce il sommario prima del testo dell’articolo.

In questo modo, quando scriviamo un nuovo post, non dobbiamo costruire manualmente il sommario. Basta usare correttamente i titoli nel contenuto e lo snippet farà il resto.

Il codice PHP per creare automaticamente il sommario

Il codice utilizza il filtro the_content, cioè uno dei filtri più utili di WordPress quando vogliamo modificare il contenuto di un articolo prima che venga mostrato nella pagina.

add_filter('the_content', 'sergio_aggiungi_sommario_articolo');

function sergio_aggiungi_sommario_articolo($content) {

if (!is_single() || !in_the_loop() || !is_main_query()) {
return $content;
}

preg_match_all('/<h([2-3])([^>]*)>(.*?)<\/h[2-3]>/', $content, $matches, PREG_SET_ORDER);

if (empty($matches) || count($matches) < 2) {
return $content;
}

$toc = '<div class="sergio-toc">';
$toc .= '<button class="sergio-toc-toggle" type="button" aria-expanded="true">';
$toc .= '<span>Sommario</span>';
$toc .= '<span class="sergio-toc-icon" aria-hidden="true">[ Nascondi ]</span>';
$toc .= '</button>';

$toc .= '<nav class="sergio-toc-content" aria-label="Sommario articolo">';
$toc .= '<ol>';

$updated_content = $content;

foreach ($matches as $index => $heading) {

$level = $heading[1];
$attrs = $heading[2];
$title_html = $heading[3];

$title_text = wp_strip_all_tags($title_html);
$slug = sanitize_title($title_text);

if (!$slug) {
$slug = 'sezione-' . ($index + 1);
}

$id = 'toc-' . $slug;

if (strpos($attrs, 'id=') === false) {
$new_heading = '<h' . $level . $attrs . ' id="' . esc_attr($id) . '">' . $title_html . '</h' . $level . '>';
$updated_content = str_replace($heading[0], $new_heading, $updated_content);
} else {
preg_match('/id=["\']([^"\']+)["\']/', $attrs, $id_match);
if (!empty($id_match[1])) {
$id = $id_match[1];
}
}

$toc .= '<li class="sergio-toc-level-' . esc_attr($level) . '">';
$toc .= '<a href="#' . esc_attr($id) . '">' . esc_html($title_text) . '</a>';
$toc .= '</li>';
}

$toc .= '</ol>';
$toc .= '</nav>';
$toc .= '</div>';

return $toc . $updated_content;
}

Quando viene eseguito lo snippet

La prima parte importante del codice è questa:

if (!is_single() || !in_the_loop() || !is_main_query()) {
return $content;
}

Questa condizione serve a evitare che il sommario venga generato ovunque.

Lo snippet deve funzionare solo nei singoli articoli del blog, non nelle pagine, nella homepage, negli archivi, nelle categorie o in altre parti del sito.

Con is_single() diciamo a WordPress di intervenire solo nei post singoli.

Con in_the_loop() e is_main_query() evitiamo invece che il codice venga applicato a contenuti secondari, query aggiuntive o blocchi che non rappresentano il contenuto principale dell’articolo.

È una piccola precauzione, ma molto importante. Quando si usa the_content, infatti, bisogna sempre fare attenzione a non modificare contenuti che non dovrebbero essere toccati.

Come vengono intercettati i titoli H2 e H3

Il cuore dello snippet è questa riga:

preg_match_all('/<h([2-3])([^>]*)>(.*?)<\/h[2-3]>/', $content, $matches, PREG_SET_ORDER);

Qui il codice cerca dentro al contenuto dell’articolo tutti i titoli h2 e h3.

La scelta di usare solo h2 e h3 è abbastanza sensata per un sommario.

Gli h2 rappresentano le sezioni principali dell’articolo, mentre gli h3 possono essere usati per eventuali sottosezioni.

Di solito non conviene includere anche h4, h5 e h6, perché il sommario rischierebbe di diventare troppo lungo e meno leggibile, soprattutto su mobile.

Subito dopo troviamo questo controllo:

if (empty($matches) || count($matches) < 2) {
return $content;
}

In pratica, se l’articolo non contiene titoli oppure ne contiene meno di due, il sommario non viene creato.

Anche questa è una scelta intelligente: se un articolo è molto breve, inserire un sommario avrebbe poco senso.

La costruzione del box del sommario

Dopo aver trovato i titoli, il codice inizia a costruire l’HTML del sommario:

$toc = '<div class="sergio-toc">';
$toc .= '<button class="sergio-toc-toggle" type="button" aria-expanded="true">';
$toc .= '<span>Sommario</span>';
$toc .= '<span class="sergio-toc-icon" aria-hidden="true">[ Nascondi ]</span>';
$toc .= '</button>';

Qui viene generato il contenitore principale con classe:

.sergio-toc

Questa è la stessa classe che abbiamo stilizzato nel CSS.

Poi viene creato il pulsante di apertura e chiusura:

.sergio-toc-toggle

Il pulsante contiene due elementi:

<span>Sommario</span>

e:

<span class="sergio-toc-icon" aria-hidden="true">[ Nascondi ]</span>

Il primo è il titolo del box, mentre il secondo è il testo che cambia quando il sommario viene aperto o chiuso tramite JavaScript.

L’attributo:

aria-expanded="true"

indica che, al caricamento della pagina, il sommario è aperto.

Perché viene usato un elemento nav

Il contenuto del sommario viene inserito dentro un elemento nav:

$toc .= '<nav class="sergio-toc-content" aria-label="Sommario articolo">';
$toc .= '<ol>';

Questa è una scelta corretta dal punto di vista semantico.

Il sommario è infatti una piccola navigazione interna dell’articolo. Non porta ad altre pagine, ma permette di muoversi tra le sezioni del contenuto.

L’attributo:

aria-label="Sommario articolo"

aiuta anche dal punto di vista dell’accessibilità, perché descrive meglio il ruolo di quella navigazione ai lettori di schermo.

La lista viene costruita con un elemento ol, cioè una lista ordinata, perché le voci seguono lo stesso ordine delle sezioni presenti nell’articolo.

Come vengono creati gli ID dei titoli

Per far funzionare i link del sommario, ogni titolo deve avere un ID.

Per esempio, un link come questo:

<a href="#toc-codice-css">Il codice CSS</a>

deve puntare a un titolo come questo:

<h2 id="toc-codice-css">Il codice CSS</h2>

Lo snippet crea automaticamente questi ID partendo dal testo del titolo.

Questa parte:

$title_text = wp_strip_all_tags($title_html);
$slug = sanitize_title($title_text);

prende il testo del titolo e lo trasforma in uno slug pulito.

Per esempio, un titolo come:

Il codice CSS del sommario

può diventare:

il-codice-css-del-sommario

Poi viene aggiunto il prefisso toc-:

$id = 'toc-' . $slug;

Quindi l’ID finale diventa:

toc-il-codice-css-del-sommario

Questo è utile perché rende gli ID più riconoscibili e riduce il rischio di conflitti con altri elementi della pagina.

Cosa succede se un titolo ha già un ID

Lo snippet controlla anche se il titolo possiede già un ID.

Questa parte è molto importante:

if (strpos($attrs, 'id=') === false) {
$new_heading = '<h' . $level . $attrs . ' id="' . esc_attr($id) . '">' . $title_html . '</h' . $level . '>';
$updated_content = str_replace($heading[0], $new_heading, $updated_content);
} else {
preg_match('/id=["\']([^"\']+)["\']/', $attrs, $id_match);
if (!empty($id_match[1])) {
$id = $id_match[1];
}
}

Se il titolo non ha un ID, lo snippet lo aggiunge automaticamente.

Se invece il titolo ha già un ID, magari inserito manualmente da Gutenberg tramite il campo “Ancora HTML”, il codice lo mantiene e lo usa per il link del sommario.

Questa è una buona soluzione perché non sovrascrive le ancore già presenti.

In pratica, se hai già scelto manualmente un’ancora per un titolo, lo snippet la rispetta.

Come vengono create le voci del sommario

Per ogni titolo trovato, lo snippet crea una voce dentro la lista:

$toc .= '<li class="sergio-toc-level-' . esc_attr($level) . '">';
$toc .= '<a href="#' . esc_attr($id) . '">' . esc_html($title_text) . '</a>';
$toc .= '</li>';

La classe cambia in base al livello del titolo.

Se il titolo è un h2, la voce avrà questa classe:

<li class="sergio-toc-level-2">

Se invece il titolo è un h3, avrà questa classe:

<li class="sergio-toc-level-3">

Questo permette di gestire graficamente le sezioni e le sottosezioni.

Per esempio, nel CSS possiamo far rientrare leggermente gli h3, così il sommario risulta più chiaro e gerarchico.

.sergio-toc-level-3 {
margin-left: 18px;
font-size: 0.95rem;
}

In questo modo l’utente capisce subito quali voci sono sezioni principali e quali invece sono sottosezioni.

Dove viene inserito il sommario

Alla fine dello snippet troviamo questa riga:

return $toc . $updated_content;

Questa istruzione restituisce il sommario seguito dal contenuto aggiornato dell’articolo.

In pratica il sommario viene inserito automaticamente prima del testo del post.

È una posizione molto comoda, perché il lettore lo vede subito appena entra nell’articolo, prima di iniziare a scorrere il contenuto.

Il vantaggio di questa soluzione

Il vantaggio principale di questo sistema è che il sommario diventa automatico.

Non serve più scrivere manualmente la lista dei link.

Non serve ricordarsi ogni volta di aggiungere le ancore ai titoli.

Non serve aggiornare il sommario quando si modifica la struttura dell’articolo.

Basta scrivere il post usando correttamente i titoli h2 e h3, e WordPress costruirà il sommario in autonomia.

Questa soluzione è particolarmente utile per articoli lunghi, guide tecniche e tutorial, dove la struttura del contenuto è molto importante.

In più, il codice lavora solo sui singoli articoli e non appesantisce inutilmente il resto del sito.

Una piccola attenzione pratica

Questo tipo di soluzione funziona bene se l’articolo è scritto con una struttura ordinata.

Conviene quindi usare gli h2 per le sezioni principali e gli h3 per gli approfondimenti interni.

Meglio evitare di usare i titoli solo per ragioni estetiche, perché il sommario li leggerà come parti della struttura del contenuto.

In altre parole, se un testo sembra solo “più grande” ma non rappresenta davvero una sezione, è meglio non trasformarlo in h2 o h3.

Un sommario automatico funziona bene quando la gerarchia dei titoli è pulita. E questa, tra l’altro, è anche una buona abitudine per SEO, accessibilità e leggibilità.

Conclusione

Aggiungere un sommario agli articoli WordPress è una personalizzazione semplice, ma molto utile.

Permette di migliorare la leggibilità, rende i contenuti lunghi più facili da consultare e dà al blog un aspetto più ordinato e professionale.

Con questo snippet possiamo creare un sommario richiudibile, responsive e leggero, senza installare plugin aggiuntivi.

Il CSS viene caricato nella head del sito, il JavaScript nel footer e tutto viene limitato ai singoli articoli del blog grazie a is_singular('post').

Il risultato è una soluzione pulita, controllabile e perfettamente adattabile allo stile del proprio sito WordPress.

Per chi pubblica guide tecniche, tutorial o articoli lunghi, un sommario di questo tipo può fare davvero la differenza: non solo dal punto di vista estetico, ma soprattutto dal punto di vista dell’esperienza utente.

Potrebbe interessarti anche...

Popup exit intent in WordPress: come crearne uno con GeneratePress e GenerateBlocks senza plugin pesanti

Popup exit intent in WordPress: come crearne uno con GeneratePress e GenerateBlocks senza plugin pesanti

15/06/2026

Popup exit intent in WordPress realizzato con GeneratePress, GenerateBlocks e uno snippet personalizzato, senza usare plugin pesanti. Nell’articolo vediamo come costruire una finestra modale leggera, personalizzabile e responsive, che appare quando l’utente sta per lasciare la pagina. La guida spiega come gestire CSS, JavaScript, pagine di esclusione, fallback mobile, animazioni morbide, accessibilità e salvataggio della visualizzazione tramite localStorage e sessionStorage.

Leggi l'articolo
Tag cloud tridimensionale in WordPress senza plugin: come crearla per i singoli articoli

Tag cloud tridimensionale in WordPress senza plugin: come crearla per i singoli articoli

08/06/2026

Scopri come creare una tag cloud tridimensionale in WordPress senza installare plugin. In questa guida viene spiegato come usare uno shortcode personalizzato per mostrare una nuvola di tag animata solo nei singoli articoli, recuperando automaticamente i tag presenti nel sito e permettendo di modificare facilmente velocità di rotazione, numero di tag e dimensione della cloud.

Leggi l'articolo
Come creare uno slider con GeneratePress e GenerateBlocks senza usare page builder pesanti

Come creare uno slider con GeneratePress e GenerateBlocks senza usare page builder pesanti

01/06/2026

Creare uno slider con GeneratePress e GenerateBlocks aggiunge dinamismo senza plugin pesanti. Lo costruiremo con Container, classi CSS, pulsanti e JavaScript leggero per scorrere le slide, mantenendolo responsive e modificabile da WordPress.

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