Motore qualità: SEO, accessibilità, performance
Il motore qualità controlla ogni documento e segnala i problemi di SEO, accessibilità e performance, bloccando la pubblicazione se servono.
In questa pagina
01Panoramica
Il motore qualità controlla un documento Nucleo e restituisce un report con punteggi, problemi e un flag blocking.
Il motore compone il documento come una pagina e ne ricava un profilo: titoli, immagini, link, coppie di colori, landmark, ancore, pulsanti, moduli e stili. Le regole leggono questo profilo, non l'HTML.
Il controllo gira a ogni salvataggio e blocca la pubblicazione se il report è bloccante. Un riconoscimento (acknowledge) rende non bloccante un singolo problema.
La parte SEO stampa title, meta description, Open Graph e JSON-LD sulle pagine singole, ma solo se Yoast SEO e Rank Math non sono attivi.
Tutte le funzioni di questo capitolo sono Free.
02Esecuzione del controllo
Quando gira il controllo
Cosa fa: esegue il controllo sul documento nei seguenti casi:
- a ogni salvataggio, creazione, patch, pubblicazione e import di un documento, anche in
dryRun; - alla ricompilazione dopo la modifica di una sezione riutilizzabile;
- con l'ability
nucleo/quality-check; - con l'ability
nucleo/doc-validate, se il documento è valido; - nella scansione del sito (
nucleo/quality-scan), per il punteggio SEO di ogni pagina.
Come si usa: non serve un'azione esplicita al salvataggio. Per un controllo senza salvare, usa nucleo/quality-check con document oppure nucleo/doc-validate.
Parametri: Nessuno.
Permessi: quelli dell'operazione che lo avvia.
Edizione: Free.
Limiti noti:
- Il motore non gira a runtime sul sito pubblico.
- Il controllo compone un solo documento. Header, footer e layout applicati alla pagina non entrano nel controllo. Le sezioni riutilizzabili (
nc/section-ref) invece sì, perché il documento arriva già espanso. - Il documento deve superare la validazione dello schema. Se non la supera,
nucleo/quality-checkrestituisceinvalid_documentsenza report.
Profilo della pagina
Cosa fa: percorre la radice e i figli del documento e raccoglie i dati per le regole. Usa la sezione semantics del manifest di ogni blocco (heading, image, link, landmark) e alcuni campi fissi del nodo.
Come si usa: interno. Lo usa il motore qualità.
Parametri:
| Dato raccolto | Fonte nel nodo | Usato da |
|---|---|---|
| Titoli | semantics.heading.levelFrom (intero 1–6) e textFrom | seo.h1.*, seo.heading.*, soglia di a11y.contrast |
| Immagini | semantics.image.srcFrom, altFrom, decorativeFrom | seo.image.alt.missing, perf.lcp.lazy |
| Link | semantics.link.hrefFrom, textFrom; content.ariaLabel o settings.ariaLabel | a11y.link.empty |
| Landmark | semantics.landmark.tagFrom, oppure il tag fisso con l'eventuale ifFrom | a11y.landmark.missing |
| Coppie di colori | style.color e style.background (per breakpoint) | a11y.contrast |
| Condizioni | visibility.when[].is | visibility.logged_in |
| Ancore | settings.anchor (senza spazi ai bordi) | a11y.anchor.duplicate |
| Posizione fissa | style.position uguale a fixed in almeno un breakpoint | layout.fixed.z_index |
| Pulsanti | nodi nc/button: content.text, settings.href | content.button.inert |
| Stili | ogni valore stringa di style, anche per breakpoint | style.token.literal |
| Moduli | nodi nc/native-form con azioni esterne | form.* |
Permessi: Nessuno.
Edizione: Free.
Limiti noti:
- L'immagine LCP stimata è la prima immagine non decorativa. Il profilo la segna con
lcp: trueelazy: false. Tutte le altre immagini hannolazy: true. - Il campo
visibledei titoli resta sempre vuoto: il profilo non calcola la visibilità per breakpoint. - Un
nc/buttonconbindsusettings.hrefnon entra nell'elenco dei pulsanti.
Gravità e blocco della pubblicazione
Cosa fa: ogni problema ha una gravità: error, warning o info. Un problema può avere anche publishBlocking. Il report è bloccante se esiste almeno un problema aperto con gravità error o con publishBlocking.
Come si usa: leggi quality.blocking nella risposta.
| Gravità | Bozza | Pubblicazione, o salvataggio di un post già pubblicato | Punteggio |
|---|---|---|---|
error | si salva | fallisce con quality_blocking, salvo acknowledge | −20 |
warning con blocksPublish | si salva | fallisce con quality_blocking, salvo acknowledge | −5 |
warning | si salva | riesce | −5 |
info | si salva | riesce | 0 |
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti:
- Un salvataggio di un post con stato
publish,futureoprivatevale come pubblicazione: il controllo bloccante si applica anche senzapublish. - Una pagina Nucleo in bozza con un report bloccante non va online neanche dall'editor di WordPress: editor a blocchi, editor classico, pannello della pagina o programmazione. La REST risponde
quality_blocking. L'editor classico mantiene lo stato precedente. - Alla ricompilazione dopo la modifica di una sezione, un report bloccante su un post pubblicato lascia online la versione precedente. Nucleo conserva i problemi sul post e restituisce
quality_blocking. I problemi conservati si cancellano al salvataggio successivo riuscito. - Il messaggio di errore è "Blocking quality issues: fix them or acknowledge them.".
Punteggio
Cosa fa: calcola tre punteggi da 0 a 100: seo, a11y e perf. Parte da 100 e toglie 20 per ogni error aperto e 5 per ogni warning aperto. Le info valgono 0. Il minimo è 0.
Come si usa: leggi quality.score.
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti:
- La categoria dipende dal prefisso del codice della regola:
seo.,a11y.operf.. Le regole con altri prefissi (content.,form.,style.,layout.,visibility.,links.) non toccano nessun punteggio. - I problemi riconosciuti con
acknowledgenon contano.
Formato del report
Cosa fa: restituisce l'oggetto quality della risposta.
Come si usa:
{
"quality": {
"score": { "seo": 75, "a11y": 100, "perf": 100 },
"blocking": true,
"issues": [
{
"rule": "seo.h1.multiple",
"severity": "error",
"nodes": [ { "doc": "page:120", "id": "hero" }, { "doc": "page:120", "id": "sub" } ],
"message": "The composed page contains 2 h1: hero in page:120, sub in page:120.",
"fix": [ { "op": "set", "doc": "page:120", "id": "sub", "path": "settings.level", "value": 2 } ]
}
]
}
}Parametri:
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
rule | string | codice della regola | — | Da usare in acknowledge. |
severity | string | error, warning, info | — | |
nodes | array | {doc, id} | [] | Vuoto per le regole di pagina (seo.h1.missing, a11y.landmark.missing) e per i controlli sul post, che usano {doc: "page:<id>", id: "meta"}. |
message | string | — | — | Tradotto, salvo i casi indicati nelle regole. |
fix | array | operazioni di patch con chiave doc in più | [] | Oggi lo compila solo seo.h1.multiple. |
guide | string | R1.2, R1.5, R2.3, R4.4, R4.5 | assente | Presente solo per le regole collegate alla guida a fasi. |
blocksPublish | boolean | true | assente | Presente solo per i warning con publishBlocking. |
Permessi: Nessuno.
Edizione: Free.
Limiti noti: quality.issues contiene solo i problemi aperti. I problemi riconosciuti non compaiono nella risposta.
Riconoscimento (acknowledge)
Cosa fa: rende non bloccante un problema. Il riconoscimento indica regola, nodo e motivazione. Il motore lo accetta se rule coincide, node è uno degli id in nodes del problema e reason non è vuoto dopo aver tolto gli spazi ai bordi.
Come si usa: passa acknowledge alle ability di scrittura (nucleo/doc-publish, nucleo/doc-patch, nucleo/doc-create, nucleo/site-import) o a nucleo/quality-check. Dalla CLI locale usa nucleo push --acknowledge FILE.
{ "acknowledge": [ { "rule": "form.action.consequential", "node": "f1", "reason": "L'utente ha autorizzato il webhook il 30/10" } ] }Parametri:
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
rule | string | codice della regola | — | Obbligatorio. |
node | string | id di un nodo del problema | — | Obbligatorio. Un riconoscimento sul nodo nc/section-ref vale per i nodi espansi della sezione. |
reason | string | non vuoto | — | Obbligatorio. |
Permessi: quelli dell'ability che lo riceve.
Edizione: Free.
Limiti noti:
- Nucleo salva i riconoscimenti usati nel post, con un'impronta (SHA-1) del nodo senza figli. Valgono per i salvataggi, gli import e le ricompilazioni successive.
- Un riconoscimento decade se il nodo cambia (tipo, contenuto, impostazioni, stile), se il nodo sparisce o se il problema non c'è più.
- Per la stessa coppia regola e nodo vince il riconoscimento più recente.
- I problemi senza nodi (
seo.h1.missing,a11y.landmark.missing) non si possono riconoscere: restano bloccanti finché non si correggono. - Le regole elencate in
nonForcible(opzionenucleo_quality, impostabile connucleo/settings-setnel gruppoquality, fino a 50 id) non si possono riconoscere: restano bloccanti. La lista è vuota di default, quindi ogni regola è forzabile. - I riconoscimenti non entrano nel registro attività.
Regole per tipo di documento
Cosa fa: toglie dal report alcune regole quando il documento non è una pagina.
Come si usa: automatico, in base a kind.
kind | Regole tolte |
|---|---|
layout, popup | seo.h1.missing, a11y.landmark.missing, seo.content.thin |
section | le tre precedenti più content.button.inert, form.action.consequential |
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti: nucleo/quality-check aggiunge i controlli sul post solo con postId e solo se kind non è layout, section o popup.
Regole aggiuntive di terze parti
Cosa fa: il filtro nucleo/quality/rules riceve l'elenco delle regole e può aggiungerne o toglierne. Nucleo scarta gli elementi che non implementano l'interfaccia Nucleo\Quality\Rule.
Come si usa:
add_filter('nucleo/quality/rules', function (array $rules): array {
$rules[] = new MiaRegola(); // implementa id(), severity(), check(ComposedPage $page): array
return $rules;
});Parametri: l'interfaccia Rule ha id(): string, severity(): Severity e check(ComposedPage $page): array, cioè una lista di problemi.
Permessi: Nessuno.
Edizione: Free.
Limiti noti: il motore si costruisce una volta per richiesta. Il filtro va aggiunto prima del primo controllo.
03Regole
Tabella riassuntiva. "Punto" indica dove nasce il problema: motore (a ogni salvataggio), sezioni (a ogni salvataggio, sulle sezioni riutilizzabili), post (solo in nucleo/quality-check con postId e nella scansione), sito (solo nella scansione).
| Codice | Gravità | Categoria | Blocca | Punto | Cosa controlla |
|---|---|---|---|---|---|
seo.h1.missing | error | seo | sì | motore | Nessun h1 |
seo.h1.multiple | error | seo | sì | motore | Più di un h1 |
seo.heading.skip | warning | seo | no | motore | Salto di livello tra titoli |
seo.heading.empty | error | seo | sì | motore | Titolo senza testo |
seo.image.alt.missing | warning | seo | no | motore | Immagine non decorativa senza alt |
a11y.contrast | error | a11y | sì | motore | Contrasto sotto 4.5:1 o 3:1 |
a11y.link.empty | error | a11y | sì | motore | Link senza testo accessibile |
a11y.anchor.duplicate | warning | a11y | no | motore | Stessa ancora su più nodi |
a11y.landmark.missing | warning | a11y | no | motore | Manca il landmark main |
perf.lcp.lazy | error | perf | sì | motore | Immagine LCP con loading="lazy" |
visibility.logged_in | info | — | no | motore | Contenuto visibile solo a utenti loggati |
layout.fixed.z_index | warning | — | no | motore | position: fixed senza zIndex |
content.button.inert | warning + blocksPublish | — | sì | motore | Pulsante senza link né azione |
form.action.consequential | warning + blocksPublish | — | sì | motore | Modulo con azioni esterne |
form.recipient.invalid | warning | — | no | motore | Destinatario email del modulo non valido |
a11y.button.as-link | warning | a11y | no | motore | Link senza destinazione (href vuoto, # o javascript:) o <a> senza href nei testi |
a11y.focus.hidden | error | a11y | sì | motore | Stile di focus che nasconde l'indicatore (outline none, trasparente o alpha 0) |
a11y.target.size | warning | a11y | no | motore | Pulsante o icona sotto 24 px nel breakpoint base |
a11y.lang.missing | warning | a11y | no | motore | Testo in inglese su un sito in un'altra lingua, senza settings.lang. Soglia: almeno 8 parole, 3 stopword distinte, almeno il 25% |
a11y.video.autoplay | warning | a11y | no | motore | Video che parte da solo con l'audio attivo (manca mute=1) |
seo.link.text.generic | warning | seo | no | motore | Testo del link generico ("clicca qui", "leggi di più", "here"...) senza aria-label |
seo.image.filename | info | seo | no | motore | Nome file dell'immagine generico (per esempio img, DSC) o esadecimale |
seo.heading.style-only | info | seo | no | motore | Testo breve con font grande (24 px o 1.5 rem) senza heading, oppure heading oltre 150 caratteri |
seo.hidden.content | warning | seo | no | motore | Titolo o testo (almeno 20 parole) nascosto su alcuni breakpoint e visibile su altri |
perf.html.block | info | perf | no | motore | Blocco nc/html o nc/shortcode: codice libero che il tema non ottimizza |
perf.dom.size | warning | perf | no | motore | Stima degli elementi DOM della pagina sopra 1500 (un avviso per pagina) |
perf.css.budget | warning | perf | no | motore | CSS servito alla pagina sopra 75 KB |
perf.js.budget | warning | perf | no | motore | JS servito alla pagina sopra 100 KB |
perf.font.count | warning | perf | no | motore | Più di 2 famiglie o più di 4 varianti di font nel sito (un avviso per sito) |
perf.image.oversize | warning | perf | no | motore | Immagine più del doppio più larga di quanto viene mostrata, senza srcset |
perf.embed.no-facade | warning | perf | no | motore | iframe sopra la piega senza facciata |
ds.layout.conflict | warning | — | no | motore | Due o più layout con la stessa area e le stesse condizioni |
style.token.literal | error | — | sì | motore | Valore di stile uguale a un token |
content.link.missing | warning | — | no | sezioni | Riferimento a un contenuto assente o non pubblicato |
content.link.path | info | — | no | sezioni | Link interno scritto come percorso |
content.section.missing | warning | — | no | sezioni | Sezione riutilizzabile assente o nel cestino |
seo.title.length | warning | seo | no | post | Title fuori da 30–60 caratteri |
seo.description.missing | warning | seo | no | post | Meta description assente |
seo.description.length | info | seo | no | post | Meta description fuori da 70–160 caratteri |
seo.content.thin | warning | seo | no | post | Meno di 300 parole |
seo.noindex | warning | seo | no | post | Pagina esclusa dai motori di ricerca |
seo.canonical.external | warning | seo | no | post | Canonical verso un altro host |
seo.links.internal.none | info | seo | no | post | Nessun link interno nel contenuto |
seo.title.missing | warning | seo | no | post | Title SEO assente |
seo.h1.title-mismatch | info | seo | no | post | Primo h1 diverso dal title SEO |
seo.noindex.important | error | seo | sì | post | Pagina noindex presente nel menu principale |
seo.canonical.conflict | warning | seo | no | post | Canonical verso un'altra pagina del sito |
seo.title.duplicate | warning | seo | no | sito | Stesso title su più pagine |
seo.h1.duplicate | warning | seo | no | sito | Stesso testo h1 su più pagine |
links.orphan | warning | — | no | sito | Pagina che nessun altro documento linka |
seo.hreflang.missing | warning | seo | no | sito | Pagine senza traduzione in tutte le lingue |
seo.schema.duplicate | error | seo | no | sito | Il plugin SEO e Nucleo stampano entrambi un BreadcrumbList sulla stessa pagina |
seo.duplicate.section | warning | seo | no | sito | Sezione ripetuta, visibile solo su schermi piccoli e solo su grandi: contenuto duplicato |
seo.h1.missing
Codice: seo.h1.missing. Gravità: error. Categoria: seo.
Cosa controlla: nessun titolo di livello 1 nel documento composto.
Soglie: almeno un h1.
Come si risolve: porta a 1 il livello di un titolo (settings.level per nc/heading).
Limiti noti: il problema non ha nodi, quindi non si può riconoscere. Non vale per layout, section e popup.
seo.h1.multiple
Codice: seo.h1.multiple. Gravità: error. Categoria: seo.
Cosa controlla: più di un titolo di livello 1. Il problema elenca tutti gli h1 in nodes.
Soglie: al massimo un h1.
Come si risolve: applica il fix. Il fix tiene il primo h1 di un documento page: (o il primo in assoluto) e imposta settings.level a 2 sugli altri. In alternativa, riconosci il problema su uno dei nodi.
Limiti noti: se un h1 sta dentro una sezione riutilizzabile, il report toglie il fix e aggiunge "(inside a reusable section)" al messaggio.
seo.heading.skip
Codice: seo.heading.skip. Gravità: warning. Categoria: seo.
Cosa controlla: due titoli consecutivi in ordine di documento in cui il livello sale di più di uno (per esempio h2 seguito da h4). Una discesa di livello non conta.
Soglie: differenza massima +1.
Come si risolve: correggi il livello del secondo titolo.
Limiti noti: il messaggio è fisso in italiano, non tradotto ("Salto di livello: da hX a hY.").
seo.heading.empty
Codice: seo.heading.empty. Gravità: error. Categoria: seo.
Cosa controlla: titolo con testo vuoto dopo la rimozione dei tag e degli spazi ai bordi.
Soglie: nessuna.
Come si risolve: scrivi il testo del titolo o rimuovi il nodo.
Limiti noti: nessuno noto.
seo.image.alt.missing
Codice: seo.image.alt.missing. Gravità: warning. Categoria: seo.
Cosa controlla: immagine non decorativa con alt assente o fatto solo di spazi.
Soglie: nessuna.
Come si risolve: scrivi l'alt, oppure marca l'immagine come decorativa nel campo indicato da semantics.image.decorativeFrom del blocco.
Limiti noti: nessuno noto.
a11y.contrast
Codice: a11y.contrast. Gravità: error. Categoria: a11y.
Cosa controlla: rapporto di contrasto WCAG 2.x tra style.color del nodo e lo sfondo. Lo sfondo è style.background del nodo o dell'antenato più vicino. Senza sfondo dichiarato vale il token $color.background.
Soglie:
- 4.5:1 per il testo;
- 3:1 per i titoli di livello 1 e 2;
- 3:1 per le icone
nc/iconcon etichetta (content.labelnon vuota o collegata conbind), secondo WCAG 1.4.11.
Come si risolve: cambia il colore o lo sfondo. Il messaggio riporta i due colori, il rapporto e il minimo.
Limiti noti:
- Il controllo crea una coppia per ogni breakpoint in cui il colore o lo sfondo cambia. Un breakpoint senza valore eredita il valore base.
- Accetta solo colori
#rgbo#rrggbb, anche come token risolti. Valori comergba(),var()o gradienti non producono segnalazioni. - Non controlla i blocchi senza testo:
nc/iconsenza etichetta,nc/image,nc/divider,nc/spacer,nc/video,nc/post-image. - Per
nc/containerenc/sectionil controllo guarda i figli. Un figlio con colore proprio ha la sua coppia. - Il messaggio è fisso in italiano, non tradotto.
a11y.link.empty
Codice: a11y.link.empty. Gravità: error. Categoria: a11y.
Cosa controlla: nodo con semantica di link senza testo visibile e senza ariaLabel in content o settings.
Soglie: nessuna.
Come si risolve: scrivi il testo del link o aggiungi content.ariaLabel.
Limiti noti: nessuno noto.
a11y.anchor.duplicate
Codice: a11y.anchor.duplicate. Gravità: warning. Categoria: a11y.
Cosa controlla: lo stesso valore di settings.anchor su due o più nodi. Un caso tipico è una sezione con ancora usata due volte.
Soglie: un nodo per ancora.
Come si risolve: rendi unica ogni ancora.
Limiti noti: nessuno noto.
a11y.landmark.missing
Codice: a11y.landmark.missing. Gravità: warning. Categoria: a11y.
Cosa controlla: la pagina composta, con i suoi layout, non ha il landmark main, nav o footer.
Soglie: almeno un main, un nav e un footer.
Come si risolve: imposta il tag main sul contenitore principale, nel campo indicato da semantics.landmark.tagFrom. Un nc/menu con un menu è un nav. Di solito il layout footer dà il footer.
Limiti noti: il problema non ha nodi. Non vale per layout, section e popup.
perf.lcp.lazy
Codice: perf.lcp.lazy. Gravità: error. Categoria: perf.
Cosa controlla: immagine segnata come LCP e come lazy.
Soglie: nessuna.
Come si risolve: non serve un intervento sul documento con il profilo attuale.
Limiti noti: il profilo segna sempre l'immagine LCP con lazy: false. Con il motore di Nucleo la regola non produce segnalazioni. Scatta solo con un profilo costruito in altro modo, come nei test.
visibility.logged_in
Codice: visibility.logged_in. Gravità: info. Categoria: nessuna (non entra nei punteggi).
Cosa controlla: nodo con visibility.when che contiene is: "logged_in" o is: "role:<ruolo>". Il contenuto resta nascosto agli anonimi e ai motori di ricerca.
Soglie: nessuna.
Come si risolve: è un'informazione. Verifica che il contenuto non serva all'indicizzazione.
Limiti noti: il messaggio è fisso in italiano, non tradotto.
layout.fixed.z_index
Codice: layout.fixed.z_index. Gravità: warning. Categoria: nessuna.
Cosa controlla: style.position uguale a fixed in almeno un breakpoint, senza la chiave style.zIndex.
Soglie: nessuna.
Come si risolve: aggiungi style.zIndex.
Limiti noti: basta la presenza della chiave zIndex, a qualunque valore.
content.button.inert
Codice: content.button.inert. Gravità: warning con blocksPublish. Categoria: nessuna. Guida: R4.5.
Cosa controlla: nc/button con settings.href vuoto, uguale a # o scartato dal renderer (per esempio javascript:).
Soglie: nessuna.
Come si risolve: aggiungi un href (per esempio page:<slug> o home) oppure usa il blocco interattivo adatto. Il messaggio suggerisce il blocco in base a parole intere del testo, nell'ordine:
| Blocco suggerito | Parole (radici, maiuscole indifferenti) |
|---|---|
nc/slider | testo fatto solo di frecce (← → ‹ › « » < >) |
nc/collection | filtr…, filter(s), ordina…, sort, azzera…, reset, clear, carica altr…, load more, mostra altr…, show more |
nc/tabs | tab(s), scheda, schede |
nc/accordion | faq, domand…, espandi, expand, open |
nc/slider | prev, previous, next, precedent…, successiv… |
nc/search | cerc…, search |
nc/native-form | invia…, send, iscriv…, subscribe, contatta…, contact, prenota…, book |
Limiti noti:
- Un
hrefcollegato conbindesclude il pulsante dal controllo. - Non vale per i documenti
section. - Un pulsante gestito da uno script tramite attributi non si riconosce. In quel caso serve
acknowledge.
form.action.consequential
Codice: form.action.consequential. Gravità: warning con blocksPublish. Categoria: nessuna. Guida: R4.4.
Cosa controlla: nc/native-form con almeno un'azione esterna in settings.actions: webhook, create-post, component, oppure email con to diverso da vuoto e da admin.
Soglie: nessuna.
Come si risolve: chiedi il consenso all'utente. Poi pubblica con acknowledge sul nodo del modulo e scrivi il consenso in reason. Il messaggio chiede anche un invio di prova.
Limiti noti: non vale per i documenti section. Il riconoscimento decade quando il nodo del modulo cambia.
form.recipient.invalid
Codice: form.recipient.invalid. Gravità: warning. Categoria: nessuna.
Cosa controlla: destinatario di un'azione email di nc/native-form che non è admin, un indirizzo email valido o field:<nome>.
Soglie: il <nome> di field: segue ^[a-z][a-z0-9_]{0,31}$.
Come si risolve: usa admin, un indirizzo valido o field:<nome> di un campo email.
Limiti noti: non blocca la pubblicazione.
style.token.literal
Codice: style.token.literal. Gravità: error. Categoria: nessuna. Guida: R1.2.
Cosa controlla: valore stringa di style uguale al valore di un token del gruppo della proprietà (colori, spaziature, raggi, ombre, font). Il confronto è sul valore intero.
Soglie: un problema per nodo, proprietà e valore, anche se il valore si ripete nei breakpoint.
Come si risolve: scrivi il riferimento al token indicato nel messaggio (per esempio $color.primary). Dalla CLI locale, nucleo push --use-tokens sostituisce i valori nei file.
Limiti noti: la regola è attiva solo se il motore riceve i token del sito. Nucleo li passa sempre.
content.link.missing, content.link.path, content.section.missing
Codici e gravità: content.link.missing (warning, guida R2.3), content.link.path (info, guida R2.3), content.section.missing (warning, guida R1.5). Categoria: nessuna.
Cosa controllano:
content.link.missing: un riferimento interno (per esempiopage:contatti) punta a un contenuto assente o non pubblicato. Il link esce come#conaria-disabled.content.link.path: un link interno scritto come percorso (/contatti) ha un riferimento equivalente.content.section.missing: unnc/section-refcita una sezione assente o nel cestino. Il blocco non disegna nulla.
Soglie: nessuna.
Come si risolvono: pubblica il contenuto o correggi il riferimento; scrivi il riferimento suggerito nel messaggio; crea o ripristina la sezione.
Limiti noti: non nascono dalle regole del motore e non passano dal filtro nucleo/quality/rules. Nucleo li aggiunge a ogni salvataggio, in nucleo/quality-check e in nucleo/doc-validate.
seo.title.length
Codice: seo.title.length. Gravità: warning. Categoria: seo.
Cosa controlla: lunghezza in caratteri del title SEO letto da Nucleo (vedi Lettura dei meta SEO).
Soglie: da 30 a 60 caratteri.
Come si risolve: senza plugin SEO, scrivi meta.seo.title del documento. Con Yoast SEO o Rank Math, modifica il title nel plugin.
Limiti noti: senza plugin SEO e senza meta.seo.title, conta il titolo del post. Il problema usa il nodo meta del documento page:<id>.
seo.description.missing, seo.description.length
Codici: seo.description.missing (gravità warning), seo.description.length (gravità info). Categoria: seo.
Cosa controllano: meta description assente; altrimenti lunghezza fuori soglia.
Soglie: da 70 a 160 caratteri.
Come si risolvono: scrivi meta.seo.description, oppure la description nel plugin SEO attivo.
Limiti noti: lo schema del documento accetta meta.seo.description fino a 170 caratteri, oltre la soglia consigliata.
seo.content.thin
Codice: seo.content.thin. Gravità: warning. Categoria: seo.
Cosa controlla: numero di parole del contenuto della pagina (HTML compilato) senza tag, con le entità decodificate.
Soglie: almeno 300 parole. Valore fisso.
Come si risolve: aggiungi testo.
Limiti noti: la soglia non cambia per tipo di contenuto. Non vale per layout, section e popup.
seo.noindex
Codice: seo.noindex. Gravità: warning. Categoria: seo.
Cosa controlla: il valore noindex letto da Nucleo (vedi Lettura dei meta SEO).
Soglie: nessuna.
Come si risolve: togli il noindex nel plugin SEO, o nelle impostazioni di lettura di WordPress.
Limiti noti: senza plugin SEO, noindex vale vero se l'opzione blog_public è '0' (sito che scoraggia i motori di ricerca). meta.seo.noindex del documento non attiva la regola. Con Yoast SEO la regola legge il noindex di Yoast; con Rank Math legge il robots di Rank Math.
seo.canonical.external
Codice: seo.canonical.external. Gravità: warning. Categoria: seo.
Cosa controlla: host del canonical diverso dall'host del sito, senza distinzione tra maiuscole e minuscole.
Soglie: nessuna.
Come si risolve: correggi il canonical nel plugin SEO.
Limiti noti: senza plugin SEO il canonical letto è sempre vuoto: la regola scatta solo con Yoast SEO o Rank Math.
seo.links.internal.none
Codice: seo.links.internal.none. Gravità: info. Categoria: seo (vale 0 nel punteggio).
Cosa controlla: nessun link interno nel contenuto della pagina. Interno significa stesso host del sito, oppure href senza host e senza schema.
Soglie: almeno un link interno.
Come si risolve: aggiungi un link a un'altra pagina del sito.
Limiti noti: un href #ancora o relativo conta come interno.
seo.title.duplicate, seo.h1.duplicate
Codici: seo.title.duplicate, seo.h1.duplicate. Gravità: warning. Categoria: seo.
Cosa controllano: stesso title SEO, o stesso testo di un h1, su due o più documenti pubblicati. Il confronto avviene dopo minuscole e riduzione degli spazi.
Soglie: due pagine.
Come si risolvono: rendi unici title e h1.
Limiti noti: solo nella scansione del sito. L'h1 si legge dall'HTML del contenuto. Il problema elenca le pagine coinvolte (lista di ID) invece dei nodi.
links.orphan
Codice: links.orphan. Gravità: warning. Categoria: nessuna.
Cosa controlla: documento pubblicato che nessun altro documento pubblicato linka. La home statica è esclusa.
Soglie: almeno un link in ingresso.
Come si risolve: aggiungi un link alla pagina da un altro documento.
Limiti noti: legge solo i link nell'HTML dei documenti Nucleo pubblicati. Menu, header e footer non contano. Accetta solo href che iniziano con / o con l'indirizzo della home; ignora gli href che iniziano con //.
seo.hreflang.missing
Codice: seo.hreflang.missing. Gravità: warning. Categoria: seo.
Cosa controlla: documenti pubblicati a cui manca la traduzione in almeno una lingua del sito. Un solo problema elenca tutte le pagine.
Soglie: si attiva con almeno 2 lingue.
Come si risolve: crea le traduzioni mancanti nel plugin multilingua.
Limiti noti: le lingue vengono dal plugin multilingua supportato e dai filtri nucleo/languages e nucleo/translations.
04Scansione del sito
Scansione a lotti
Cosa fa: legge tutti i post con stato publish e un documento Nucleo, di ogni tipo di contenuto, in ordine di ID. Calcola le regole di sito su tutti i documenti a ogni chiamata. Calcola i punteggi solo per le pagine del lotto richiesto.
Come si usa: ability nucleo/quality-scan con page. Ripeti la chiamata con page: next finché next è null. Esito:
{
"site": [ { "rule": "links.orphan", "severity": "warning", "message": "Page without internal links from other documents.", "posts": [42] } ],
"pages": [ { "postId": 42, "score": 85, "a11y": 100, "perf": 100 } ],
"next": 2
}Parametri:
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
page | integer | ≥ 1 | 1 | Numero del lotto. |
Permessi: edit_posts. Scope OAuth nucleo:read.
Edizione: Free.
Limiti noti:
- Il lotto è di 50 pagine. Il parametro di dimensione del lotto non è esposto dall'ability.
- Ogni pagina riporta i punteggi
seo,a11yeperf. Somma le regole del motore e dei controlli sul post. Non applicaacknowledgené la limitazione per tipo di documento. - Con
page: 1l'ability salva per un giorno, nel transientnucleo_quality_badge, il numero di pagine coinvolte da problemi di sito con gravitàerror. I problemi di sito sonowarning, tranneseo.schema.duplicate, che èerror.
Controllo di un documento (nucleo/quality-check)
Cosa fa: restituisce il report di un documento salvato (postId) o passato in input (document), senza scrivere.
Come si usa: vedi l'ability nucleo/quality-check.
Parametri: postId (intero ≥ 1) oppure document (oggetto), mai tutti e due; acknowledge (lista di {rule, node, reason}).
Permessi: con postId, edit_post su quel post; senza, edit_posts. Scope OAuth nucleo:read.
Edizione: Free.
Limiti noti:
- I riconoscimenti salvati nel post non entrano: contano solo quelli passati nella chiamata.
- I controlli sul post girano solo con
postId. - Errori:
invalid_input(nessuno o entrambi i parametri),not_found,invalid_document.
Pagina admin Quality and SEO
Cosa fa: la pagina esegue la scansione a lotti, mostra i problemi di sito, i punteggi delle pagine e il dettaglio di una pagina con nucleo/quality-check.
Come si usa: menu Nucleo AI → Quality and SEO.
Parametri: schede issues, pages, overrides, rules.
Permessi: pagina edit_posts; avvio della scansione edit_others_posts. Schede issues e overrides: edit_others_posts. Scheda rules: edit_theme_options. Il contatore nel menu compare solo con edit_others_posts.
Edizione: Free.
Limiti noti:
- La scheda
overridesmostra "Not available yet". - Le colonne Accessibility e Performance della scheda
pagesmostrano i punteggia11yeperfdella scansione. Un valore assente resta "—". - Con la scansione, la scheda
rulesmostra la gravità reale di ogni regola. Senza scansione mostra una lista fissa di dieci regole con una gravità "tipica", che per quattro non coincide con quella del codice:seo.image.alt.missing(error invece di warning),perf.lcp.lazy(warning invece di error),seo.noindex(info invece di warning),seo.links.internal.none(warning invece di info).
05SEO in pagina
Lettura dei meta SEO
Cosa fa: restituisce title, description, source, noindex e canonical di un post. Con Yoast SEO attivo legge Yoast; con Rank Math attivo legge Rank Math; altrimenti legge meta.seo del documento.
Come si usa: interno. Lo usano i controlli sul post e la scansione del sito. Il filtro nucleo/seo/meta può cambiare il risultato.
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti: senza plugin SEO, canonical è sempre vuoto e noindex segue l'impostazione di WordPress che scoraggia i motori di ricerca (blog_public).
Title, meta description, Open Graph, robots
Cosa fa: sulle pagine singole con documento Nucleo e meta.seo non vuoto, sostituisce il title del documento HTML e stampa nel wp_head, con priorità 1, questi tag:
| Tag | Valore | Quando |
|---|---|---|
<meta name="description"> | meta.seo.description | se non vuoto |
og:title | meta.seo.title, altrimenti titolo del post | sempre |
og:description | meta.seo.description | se non vuoto |
og:url | permalink | sempre |
og:type | website | sempre |
og:image | meta.seo.image | se non vuoto |
<meta name="robots" content="noindex"> | — | se meta.seo.noindex è true |
Come si usa: compila meta.seo nel documento.
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
meta.seo.title | string | max 70 caratteri | titolo del post | |
meta.seo.description | string | max 170 caratteri | — | |
meta.seo.image | string | URI | — | |
meta.seo.noindex | boolean | false | Esclude anche dalla sitemap. |
Permessi: Nessuno (frontend).
Edizione: Free.
Limiti noti:
- Si spegne se Yoast SEO o Rank Math è attivo.
- Il canonical resta a WordPress: Nucleo non lo stampa.
- Nessun tag Twitter Card.
og:typeè semprewebsite. - Archivi e home del blog non ricevono questi tag: valgono solo sulle pagine singole.
JSON-LD della pagina
Cosa fa: stampa nel footer della pagina un solo script application/ld+json con @context e @graph. Il grafo contiene, in ordine:
- i nodi di
meta.seo.schema, conurluguale al permalink se manca; il valore"@business", a ogni profondità, diventa{"@id": "<home>/#business"}; - il nodo dell'azienda, sulla home statica o se un nodo usa
"@business"; - i nodi degli script
ld+jsonpresenti nel contenuto, tolti dall'HTML; uno script con@graphaggiunge ogni nodo del proprio grafo; - un
BreadcrumbListautomatico.
Come si usa: compila meta.seo.schema e, se serve, meta.seo.breadcrumbs.
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
meta.seo.schema | array di oggetti | max 10 nodi; @type obbligatorio con pattern ^[A-Z][A-Za-z]{2,60}$ | [] | |
meta.seo.breadcrumbs | boolean | false | — | false toglie il breadcrumb automatico. |
Permessi: Nessuno (frontend).
Edizione: Free.
Limiti noti:
- Si spegne se Yoast SEO o Rank Math è attivo.
- Gira su ogni pagina singola, anche senza
meta.seo: il grafo può contenere solo il breadcrumb. - Breadcrumb: nome del sito, antenati del post, pagina. Manca sulla home statica e se il contenuto contiene già un
BreadcrumbList. - Nucleo unisce i nodi: toglie i nodi identici e fonde tutte le
FAQPagein una sola. - Gli script con JSON non valido restano nel contenuto.
- L'output escapa
<e>: un</script>nei testi non chiude lo script.
Consolidamento JSON-LD alla compilazione
Cosa fa: alla compilazione del documento toglie dall'HTML gli script ld+json validi e aggiunge in fondo un solo script con @graph. Fonde le FAQPage (per esempio due nc/accordion) e toglie i nodi identici.
Come si usa: automatico, alla compilazione. Il filtro nucleo/schema/skip_types riceve la lista dei @type da escludere.
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti:
- Con Yoast SEO o Rank Math attivo, la lista di default esclude
WebPage,WebSite,OrganizationeBreadcrumbList. Senza plugin SEO la lista è vuota. - L'esclusione vale al momento della compilazione. Se attivi un plugin SEO dopo, i documenti già compilati restano invariati fino al salvataggio successivo.
- Gli script con JSON non valido o con una lista al primo livello restano dove sono.
Dati dell'azienda
Cosa fa: salva nell'opzione nucleo_business i dati dell'azienda e ne ricava il nodo schema.org per il JSON-LD. Il nodo ha @id <home>/#business, url di default la home, address con @type: PostalAddress e geo con @type: GeoCoordinates.
Come si usa: chiave business di nucleo/site-options-set. null cancella i dati.
| Nome | Tipo | Valori ammessi | Default | Note |
|---|---|---|---|---|
name | string | 1–300 caratteri | — | Obbligatorio. |
@type | string | ^[A-Z][A-Za-z]{2,60}$ | LocalBusiness | |
legalName, vatID, taxID, description, telephone, email, priceRange | string | 1–300 caratteri | — | |
url, logo, image | string | URL http o https | url = home | |
sameAs | array | URL http o https | — | Una stringa singola non vale. |
address | object | streetAddress, addressLocality, addressRegion, postalCode, addressCountry (stringhe) | — | Altre chiavi rifiutate. |
geo | object | latitude (±90) e longitude (±180), numeri | — | Entrambe obbligatorie, nessun'altra chiave. |
openingHours | array | Mo-Fr 08:00-18:00 (giorni Mo…Su, ore 00–23) | — | |
foundingDate | string | AAAA, AAAA-MM o AAAA-MM-GG | — | |
areaServed | string o array di stringhe | 1–300 caratteri | — | |
| altre chiavi | camelCase ^[a-z][A-Za-z]{1,40}$ | testo, lista di testi o oggetto di scalari | — |
Permessi: manage_options. Scope OAuth nucleo:admin.
Edizione: Free.
Limiti noti:
- Un valore non valido restituisce
invalid_inputcon il nome della chiave:"<chiave>" is unknown or has an invalid value.. - Il nodo esce solo dove il JSON-LD di Nucleo è attivo, quindi non con Yoast SEO o Rank Math.
Sitemap
Cosa fa: Nucleo esclude dalla sitemap di WordPress i post con meta.seo.noindex impostato a true. Aggiorna l'esclusione a ogni salvataggio e ricompilazione.
Come si usa: imposta meta.seo.noindex: true nel documento.
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti:
- L'esclusione vale solo sulle richieste pubbliche della sitemap, e solo senza Yoast SEO o Rank Math.
- Nucleo non genera una propria sitemap e non modifica
robots.txt. Il solo controllo sui robot è il metanoindexdella pagina.
Coesistenza con Yoast SEO e Rank Math
Cosa fa: Nucleo rileva il plugin SEO attivo. Con un plugin attivo:
- Nucleo non stampa title, description, Open Graph né robots;
- non stampa il JSON-LD della pagina e non toglie gli script dal contenuto;
- nel JSON-LD compilato esclude
WebPage,WebSite,OrganizationeBreadcrumbList; - non applica l'esclusione dalla sitemap;
- i controlli sul post e la scansione leggono title, description, noindex e canonical dal plugin.
Come si usa: automatico.
Parametri: Nessuno.
Permessi: Nessuno.
Edizione: Free.
Limiti noti: se entrambi i plugin sono attivi, vince Yoast SEO.
Questa pagina ti è stata utile?