La maggior parte dei SEO con cui parlo conosce lo Shadow DOM solo vagamente — "qualcosa che c'entra con i web components". I crawler ci inciampano. Gli strumenti di audit non vedono contenuti a causa sua. E, una volta che inizi a cercarlo, lo trovi ovunque.

Qui di seguito un post esplicativo, e alla fine un bookmarklet da un click per verificare qualsiasi pagina tu voglia.

Immagina una stanza antipanico

Il modo più semplice per comprendere cosa sia lo Shadow DOM è pensare alla stanza antipanico del film Panic Room di Jodie Foster (2002). Da fuori vedi tutta la casa. Non sai nemmeno che la stanza esista. Da dentro la stanza puoi guardare fuori — ma la stanza ha la propria alimentazione, la propria linea telefonica, la propria ventilazione. È isolata. Qualunque cosa succeda dentro, resta dentro.

Questo è lo Shadow DOM. Un pezzo della pagina con il proprio albero DOM dallo scope limitato, con il proprio CSS, il proprio confine sugli eventi. La pagina web e lo shadow tree lavorano assieme ma si accorgono l'una dell'altro solo in condizioni specifiche e controllate.

Perché uno sviluppatore utilizza la Shadow DOM?

Lo Shadow DOM non è un effetto collaterale, è una scelta. Uno sviluppatore ci arriva quando gli serve isolamento.

Immagina un widget video inserito in mille pagine ospiti diverse. Senza isolamento, il CSS del sito ospite può entrare e rompere i controlli. Il CSS del widget può uscire e rimodellare i paragrafi del sito. Ti ritrovi a giocare a whack-a-mole per sempre. Con uno Shadow DOM, gli stili non attraversano il confine in nessuna delle due direzioni. Il widget mantiene il suo aspetto. Il sito mantiene il suo. Pace.

È esattamente per questo motivo che elementi nativi come <video>, <input type="range"> e <details> lo usano sotto il cofano. Ed è per questo che molti web components ben costruiti — Lit, Stencil, Salesforce Lightning ci si appoggiano.

Non è la stessa cosa che del codice JavaScript inserito nella pagina

Una confusione comune che vale la pena chiarire: il JavaScript che inietta nodi DOM nella pagina non sta automaticamente usando lo Shadow DOM. Renderizzare contenuti in ritardo, lato client, o fare hydration da un framework — niente di tutto questo implica uno shadow root. Lo Shadow DOM è un'API specifica (element.attachShadow({ mode: 'open' })) che crea un sottoalbero isolato. Tutto il resto è… DOM normale, in qualsiasi momento compaia.

Questa distinzione conta perché il fix è diverso a seconda di quale delle due situazioni stai guardando.

Perché un SEO dovrebbe preoccuparsene?

Due motivi, uno ovvio e uno meno.

Quello ovvio: il contenuto dentro uno Shadow DOM può essere invisibile agli strumenti che leggono solo l'HTML serializzato. I serializer dei browser headless come page.content() — in Playwright, Puppeteer e gli altri — i classici endpoint "mostrami l'HTML renderizzato" nella maggior parte dei crawler, e persino alcune estensioni del browser, saltano gli shadow tree in silenzio. Se le descrizioni prodotto, gli accordion delle FAQ o i widget di recensioni vivono dentro uno Shadow DOM, il tuo strumento di audit può segnalarli come mancanti mentre in realtà sono lì, ben visibili sulla pagina. È esattamente il tipo di falso negativo che porta a ore di "perché questo non viene rilevato?"

Quello meno ovvio: i crawler AI non sono Googlebot. GPTBot, ClaudeBot, PerplexityBot e la maggior parte della popolazione di crawler GEO sono semplici fetcher HTTP — leggono la risposta HTML iniziale e si fermano lì. Non eseguono JavaScript. Non costruiscono un render tree. E men che meno scendono dentro gli shadow root. Quindi qualunque contenuto ti aspetti di far emergere nelle risposte delle AI e che si trova dentro uno Shadow DOM, per loro è invisibile. Googlebot nasconde il problema perché renderizza davvero la pagina, ed è il motivo per cui nessuno se ne accorge — la SEO classica continua a funzionare. La GEO non gode della stessa cortesia. Man mano che il peso della discoverability si sposta verso le superfici AI, lo Shadow DOM passa da "curiosità minore" a "prima cosa da controllare quando una scheda prodotto non viene citata".

In ogni caso, lo step uno è sapere se stai avendo a che fare con uno Shadow DOM oppure no.

Un bookmarklet per individuarli

Salvalo nella barra dei preferiti. Cliccalo su qualsiasi pagina. Ti dirà quanti shadow root aperti ci sono sulla pagina e stamperà ogni elemento host in console, così puoi ispezionarli.

javascript:(()=>{const found=[...document.querySelectorAll('*')].filter(el=>el.shadowRoot);if(found.length===0){alert('Nessuno Shadow DOM aperto trovato su questa pagina.');}else{console.clear();console.log(`%c🌑 Shadow DOM — ${found.length} trovati`,'font-size:16px;font-weight:bold;color:#a78bfa');found.forEach((el,i)=>{console.log(`%c#${i+1}`,'color:#f59e0b;font-weight:bold',el);});console.log('%c👆 Clicca un elemento sopra per ispezionarlo','color:#6b7280;font-style:italic');alert(`Trovati ${found.length} elementi Shadow DOM. Controlla la console!`);}})();

Per installarlo: crea un nuovo preferito, incolla lo snippet qui sopra come URL, chiamalo qualcosa tipo Shadow DOM check. Cliccalo su qualsiasi pagina per eseguirlo.

Due avvertenze che vale la pena conoscere

1. Modalità open vs closed. Il bookmarklet trova solo gli shadow root aperti — quelli in cui element.shadowRoot restituisce qualcosa. Gli shadow root chiusi (attachShadow({ mode: 'closed' })) sono invisibili al JavaScript esterno, per progettazione. In pratica, la modalità closed è rara; la maggior parte dei web components usa la modalità open perché è quella che tooling e test si aspettano. Ma esiste, e nessuno script lato client riuscirà a farli emergere. Se una pagina ha genuinamente solo shadow root chiusi, il bookmarklet alzerà le spalle e segnalerà zero. Per la grande maggioranza degli audit va benissimo. Per gli altri casi, si passa ai DevTools.

2. Annidamento. Il bookmarklet controlla solo il documento di primo livello. Gli Shadow DOM possono contenere altri Shadow DOM — è turtles all the way down — e una querySelectorAll('*') piatta non ci arriva. Se vuoi catturare anche gli shadow root annidati, devi scendere ricorsivamente dentro ogni shadow root che trovi e interrogarlo a sua volta. La soluzione generale è smettere di affidarsi al serializer HTML di default e invece valutare uno script dentro la pagina che attraversi l'albero ricorsivamente, estragga ogni sottoalbero shadow e ricuci l'HTML. La one-liner va bene per un controllo rapido. Per un crawling sistematico serve la versione ricorsiva.


Se vuoi vedere lo Shadow DOM nella sua versione selvatica, prova il bookmarklet su YouTube, su qualsiasi vetrina Shopify o su un sito Salesforce Experience Cloud. Li troverai lì, nascosti in bella vista.