Perché il mio critical CSS ha rotto il menu / overlay (appare aperto al caricamento)?
La sintesi in una frase: i generatori di critical CSS mantengono solo gli stili per ciò che è visibile nella parte superiore della pagina al caricamento. Gli elementi nascosti di default e aperti al click (menu mobile, overlay di ricerca, modale, drawer) vengono giudicati inutili e rimossi. Il risultato: al caricamento, appaiono aperti, rotti, privi di stile.
Lei attiva il critical CSS (che inietta solo gli stili per il primo rendering) oppure l'opzione “Remove unused CSS” per guadagnare punti su PageSpeed. Il punteggio sale. E all'improvviso: il suo overlay di ricerca appare spalancato al caricamento, il suo menu mobile si apre da solo, un pulsante riprende il suo brutto bordo grigio di default. Lei ricarica, svuota la cache… non cambia nulla.
La parte più sconcertante: quando ispeziona il codice, la regola CSS è proprio lì nell'HTML. Ma il browser non la applica. Ecco la spiegazione completa, e la soluzione che dura nel tempo.
Cosa fa davvero un generatore di critical CSS
L'idea del critical CSS è legittima ed efficace: invece di caricare l'intero foglio di stile prima del primo rendering (il che blocca la visualizzazione), Lei estrae solo il CSS necessario per la parte visibile in alto nella pagina, lo inserisce inline nel <head>, e carica il resto più tardi (in modalità defer).
Per decidere cosa è “necessario”, lo strumento renderizza la pagina e osserva ciò che è visibile nel viewport al momento del caricamento. Tutto ciò che non è visibile in quell'istante viene considerato non critico, oppure, in modalità “Remove unused CSS”, del tutto inutile e rimovibile.
Ed è proprio lì che si nasconde la trappola.
La vera causa: “nascosto di default” = “giudicato inutile”
Pensi a come appaiono i suoi componenti interattivi al caricamento della pagina:
- il menu mobile: chiuso (
display:noneotransformfuori schermo); - l'overlay di ricerca: nascosto (
opacity:0; visibility:hidden); - il modal, il drawer, il mega-menu: nascosti;
- Chiusi i tab e gli accordion: contenuto mascherato.
Tutti questi elementi sono invisibili nel momento in cui il generatore osserva la pagina. Il generatore ne deduce, in modo logico ma sbagliato, che il loro stile sia inutile, e lo rimuove dal critical CSS.
Il problema: questi elementi dovrebbero apparire più tardi, al click, tramite JavaScript. Ma il loro stile è sparito. Due sintomi classici:
Cosa si rompe
La regola che li nascondeva è sparita. Una
.overlay { opacity:0; visibility:hidden }rimossa → non c'è più nulla a nasconderlo → appare aperto al caricamento.La regola d'aspetto è sparita. Una
button { border:0; background:none }non mantenuta → il pulsante riprende lo stile di default del browser (bordo grigio, riempimento di sistema).
Il sintomo più esasperante: “la regola c'è, ma viene ignorata”
È il punto che costa ore. Lei ispeziona, e vede chiaramente .overlay { visibility:hidden } da qualche parte nell'HTML minificato. Perché allora il browser non la applica?
Perché in una pipeline di critical CSS esistono due fogli di stile:
- il critical inline (nel
<head>, applicato immediatamente), privato delle sue regole interattive; - il foglio di stile completo, destinato ad arrivare in modalità defer.
Tre cose vanno storte, spesso insieme:
- Il foglio di stile completo arriva troppo tardi (dopo che il JS ha già provato ad aprire/chiudere l'elemento), oppure non arriva affatto in alcuni percorsi di rendering.
- In modalità “Remove unused CSS”, la regola è stata rimossa da ENTRAMBI i fogli di stile: non esiste più da nessuna parte.
- La cascata si rivolta contro di Lei: una regola presente ma meno specifica (o caricata dopo) perde contro lo stile inline o quello di default.
Quindi “la regola è nell'HTML” non significa “la regola vince”. Può essere presente e scartata dalla cascata, oppure presente in un foglio non ancora caricato nel momento critico.
Le trappole della diagnosi (perché svuotare la cache non basta)
Prima di correggere, deve osservare il rendering reale, e lì la attendono tre trappole:
- Cache mobile e desktop separate. Molti motori generano un diverso critical CSS per mobile e desktop. Lei corregge, testa su desktop, tutto ok… ma la cache mobile continua a servire la vecchia versione rotta. Testi e faccia il “warm” di entrambe, con lo User-Agent corretto (l'identificativo che il browser invia per segnalare se è mobile o desktop).
- “Svuotare la cache” non sempre cancella il critical CSS per singola pagina. Il critical è spesso memorizzato per URL, separatamente dalla cache HTML. Un “clear all” può lasciare in posizione file critical obsoleti. Deve puntare esplicitamente al CSS generato.
- Il critical deriva da una fonte in cache. Se viene rigenerato da una versione obsoleta del suo foglio di stile sorgente, la sua correzione non apparirà mai. Deve forzare la rigenerazione dalla sorgente aggiornata.
Per vedere il rendering reale senza farsi ingannare dalla cache: aggiunga un parametro fittizio all'URL (?cb=123) per forzare un rendering nuovo, e lo confronti con l'URL normale.
La soluzione che dura (e viaggia con il suo tema)
La tentazione è mettere i selettori nella “safelist” (lista di esclusione) di un plugin. Funziona… finché tiene quel plugin, con quella configurazione. Il giorno in cui cambia strumento o ricostruisce il sito, si rompe di nuovo.
La soluzione robusta e portabile consiste nel togliere le sue regole davvero critiche dal CSS ottimizzabile, e inserirle Lei stesso inline in modo protetto:
a. Identifichi le regole “da non toccare mai”
- lo stato nascosto di default di ogni elemento interattivo (
display:none,opacity:0; visibility:hidden…); - il reset di pulsanti/controlli (
border:0; background:none…); - lo styling completo dell'elemento una volta aperto (così è corretto fin dalla prima apertura);
- renda questi componenti autosufficienti: un pulsante deve portare il proprio stile, senza dipendere da una classe di utilità che potrebbe a sua volta essere ottimizzata.
b. Le inserisca inline dopo che l'head è stato renderizzato
Collochi queste regole in un <style> blocco dopo la chiamata che genera l'<head> (in WordPress: dopo wp_head(), tipicamente in header.php). In questo modo arrivano per ultime e vincono la cascata rispetto al critical inserito inline più in alto.
c. Contrassegni il blocco come non ottimizzabile
Dica al motore di ottimizzazione di non toccarlo. La maggior parte rispetta attributi dedicati:
<style data-no-optimize="1" data-no-minify="1" data-no-defer="1">
/* hidden state + button reset + full overlay style, hard-coded values */
.search-overlay { opacity: 0; visibility: hidden; /* ... */ }
.search-overlay.is-open { opacity: 1; visibility: visible; }
.search-toggle { border: 0; background: none; color: #1a1a1a; }
</style>d. Fissi i valori in hard-code
Non usi variabili CSS (var(--gold)) in questo blocco: se il suo :root stesso passa attraverso l'ottimizzatore, la variabile può risultare non definita e il suo stile collassa. Fissi il colore in hard-code.
Vantaggio decisivo: questa soluzione vive nel tema. Funziona con l'ottimizzatore, senza l'ottimizzatore, e sopravvive a un cambio di strumento.
Dove Mantys Core fa la differenza
Ogni motore di critical CSS affronta questo dilemma: non può indovinare che un elemento nascosto verrà aperto al click. Quindi la vera domanda non è “succede?” (succede con tutti), ma “lo strumento le offre leve pulite per gestirlo?”.
Mantys Core è costruito proprio per questo:
esso rispetta i blocchi contrassegnati
data-no-optimize/data-no-minify/data-no-defer(esattamente il meccanismo della soluzione portabile descritto sopra) e tratta il proprio blocco critical allo stesso modo;esso le permette di mirare alla rigenerazione del critical pagina per pagina, dalla sorgente aggiornata, senza restare intrappolato in un critical obsoleto;
e gestisce esplicitamente la separazione mobile / desktop invece di lasciarla indovinare quale cache serve cosa.
In altre parole: fa il lavoro pesante di alleggerimento, lasciandole un controllo chirurgico sui componenti interattivi, invece di rimuovere tutto e lasciarla a riparare i danni.
In sintesi
- Il critical CSS mantiene solo lo stile di ciò che è visibile al caricamento. I suoi elementi nascosti di default (menu, overlay, modal) vengono giudicati inutili e rimossi.
- Sintomo ingannevole: la regola è nell'HTML ma ignorata: due fogli, una cascata che si rivolta, o una semplice rimozione.
- Diagnosi: attenzione alle cache mobile/desktop separate, al critical per URL che il “clear all” non elimina, e alla sorgente obsoleta.
- Soluzione duratura: inserisca inline lo stato nascosto + stile completo dei componenti interattivi, dopo
wp_head(), in un blocco non ottimizzabile, con valori hard-coded. Viaggia con il tema.