Tra le cause di spostamento del layout, i web font sono una delle più diffuse e delle più sottovalutate. Il meccanismo è semplice: finché il font del sito non è arrivato, il browser mostra il testo con un font di ripiego, come Arial o il font di sistema; quando il font del sito arriva, il testo viene ridisegnato. Se i due font hanno proporzioni diverse, le righe vanno a capo in punti diversi, i paragrafi cambiano altezza e tutto ciò che segue si sposta. Su una pagina ricca di testo, lo spostamento può coinvolgere l’intera area visibile.
Nell’articolo sul subsetting dei font abbiamo visto come ridurre il peso dei font e come scegliere il comportamento durante il caricamento con font-display. Qui approfondiamo l’altra metà del problema: come fare in modo che il font di ripiego occupi esattamente lo spazio del font definitivo, così che lo scambio non sposti nulla.
Perché il testo si sposta
Ogni font ha metriche proprie. La larghezza media dei caratteri determina quante parole stanno su una riga, e quindi dove il testo va a capo. L’ascendente e il discendente, cioè quanto i caratteri salgono sopra la linea di base e scendono sotto, insieme all’eventuale spazio aggiuntivo tra le righe, determinano l’altezza di ogni riga quando l’interlinea non è fissata in modo esplicito. Due font della stessa dimensione nominale, per esempio 18 pixel, possono avere caratteri più larghi o più stretti del dieci o quindici per cento e righe di altezza diversa.
Lo spostamento conta nel CLS come qualunque altro, perché avviene senza un’azione dell’utente. Spesso lo scambio del font avviene quando la pagina è già visibile da qualche centinaio di millisecondi, e l’utente ha cominciato a leggere: il testo che salta è un’esperienza fastidiosa anche quando il valore misurato è modesto.
Le tre strade possibili
Per evitare lo spostamento ci sono tre strade, con compromessi diversi.
- Non mostrare il ripiego: con
font-display: blockil testo resta invisibile finché il font non arriva, fino a circa tre secondi. Non c’è spostamento dovuto allo scambio, ma il testo compare tardi, e se il testo è l’elemento più grande della pagina l’LCP ne risente. È una scelta raramente giustificata per il testo, ragionevole solo per i font di icone. - Non fare lo scambio: con
font-display: optionalil browser usa il font del sito solo se arriva quasi subito; altrimenti mantiene il ripiego per tutta la visita. Non ci sono spostamenti, ma alla prima visita molti utenti vedono il font di ripiego. - Fare uno scambio invisibile: con
font-display: swapil testo compare subito con il ripiego e viene sostituito quando arriva il font del sito; se il ripiego ha le stesse metriche, lo scambio cambia l’aspetto dei caratteri ma non la loro posizione. È la soluzione che unisce testo immediato e assenza di spostamenti, ed è quella che vediamo nel dettaglio.
I descrittori che allineano il ripiego
La regola @font-face accetta alcuni descrittori che modificano le metriche di un font. Applicati a un font locale, come Arial, permettono di creare un font di ripiego su misura, con le proporzioni del font del sito:
size-adjustscala tutti i caratteri di una percentuale, e serve ad allineare la larghezza media: se i caratteri del font del sito sono in media il 4% più larghi di quelli di Arial, unsize-adjustdel 104% rende le righe della stessa lunghezza;ascent-overrideedescent-overridefissano l’ascendente e il discendente, e quindi l’altezza e la posizione verticale delle righe;line-gap-overridefissa lo spazio aggiuntivo tra le righe previsto dal font, spesso da portare a zero.
/* Il font del sito */
@font-face {
font-family: 'IBM Plex Sans';
src: url( '/fonts/ibm-plex-sans-latin.woff2' ) format( 'woff2' );
font-weight: 400;
font-display: swap;
}
/* Il ripiego allineato (valori di esempio, da calcolare per il proprio font) */
@font-face {
font-family: 'Plex ripiego';
src: local( 'Arial' ), local( 'Helvetica' ), local( 'Roboto' );
size-adjust: 104%;
ascent-override: 98%;
descent-override: 26%;
line-gap-override: 0%;
}
body {
font-family: 'IBM Plex Sans', 'Plex ripiego', sans-serif;
}
I valori non vanno indovinati: si ricavano dalle metriche contenute nei file dei due font, cioè le dimensioni dell’unità di progetto, l’ascendente, il discendente e la larghezza media dei caratteri. Esistono strumenti che fanno il calcolo automaticamente, come le librerie Fontaine e Capsize, diversi generatori online e alcuni framework, che creano il ripiego allineato in fase di compilazione. Il risultato va poi verificato a occhio, perché le larghezze medie sono una statistica: con testi reali il ripiego può comunque andare a capo in qualche punto diverso, ma le differenze si riducono di molto.
Il problema dei sistemi operativi
C’è una complicazione: i font locali cambiano da un sistema operativo all’altro. Arial è presente su Windows e macOS, ma non sulla maggior parte dei dispositivi Android, dove il font di sistema è Roboto; su iOS il font di sistema è San Francisco, e Helvetica è disponibile. Un ripiego basato solo su local('Arial') su Android non trova il font e passa al successivo nella lista, senza le correzioni.
Poiché i valori di correzione dipendono dal font locale usato, la soluzione più precisa è definire un ripiego per ogni font locale, ciascuno con i propri valori, e indicarli tutti nella lista: il browser usa il primo che trova. L’esempio precedente, che raggruppa più font locali sotto gli stessi valori, è un compromesso accettabile solo se le loro metriche sono simili; Arial e Helvetica lo sono, Roboto un po’ meno.
@font-face {
font-family: 'Plex ripiego Arial';
src: local( 'Arial' );
size-adjust: 104%;
ascent-override: 98%;
descent-override: 26%;
line-gap-override: 0%;
}
@font-face {
font-family: 'Plex ripiego Roboto';
src: local( 'Roboto' );
size-adjust: 103%;
ascent-override: 99%;
descent-override: 26%;
line-gap-override: 0%;
}
body {
font-family: 'IBM Plex Sans', 'Plex ripiego Arial', 'Plex ripiego Roboto', sans-serif;
}
Il supporto dei browser va considerato: size-adjust è supportato da tutti i browser principali, mentre il supporto dei descrittori di override delle metriche verticali è arrivato in tempi diversi, e Safari è stato l’ultimo. Dove un descrittore non è supportato viene ignorato: il ripiego resta parzialmente corretto, senza errori.
Fissare l’interlinea
Un accorgimento semplice riduce da solo una parte del problema: impostare un’interlinea esplicita e senza unità, come line-height: 1.6, sul testo. Con l’interlinea fissata, l’altezza di ogni riga dipende dalla dimensione del carattere e non dalle metriche verticali del font, e lo scambio non cambia l’altezza delle righe. Resta da allineare la larghezza, che determina il numero di righe, ed è il compito di size-adjust. Con il valore predefinito normal, invece, ogni font impone la propria interlinea, e lo scambio la cambia.
Esiste anche la proprietà CSS font-size-adjust, applicata agli elementi e non alla regola @font-face, che adatta la dimensione di qualunque font in modo che l’altezza delle lettere minuscole sia la stessa. È ora supportata dai principali browser ed è un’alternativa più semplice, ma meno precisa, perché allinea l’altezza delle minuscole e non la larghezza media, che è quella che determina gli a capo.
Gli altri spostamenti legati ai font
Alcuni spostamenti non dipendono dal ripiego. Se il sito usa il grassetto o il corsivo ma carica solo il peso normale, il browser crea un grassetto artificiale, più largo o più stretto di quello vero; se il grassetto vero arriva dopo, il testo si sposta di nuovo. Conviene caricare i pesi effettivamente usati, o usare un font variabile che li contiene tutti, e verificare che non ci siano pesi dichiarati nel CSS ma non disponibili.
Un’altra fonte di problemi sono i caricatori di font in JavaScript, usati da alcuni servizi di font e da vecchi temi, che aggiungono classi al documento quando i font sono pronti. Lo scambio avviene allora dopo l’esecuzione dello script, più tardi di quanto farebbe il browser da solo, e spesso in più passaggi. Con i font serviti direttamente tramite @font-face, preferibilmente dal proprio dominio e con un preload per il font principale, lo scambio avviene prima, e prima avviene meno testo si sposta, perché l’utente ha avuto meno tempo per scorrere.
E il font di sistema?
La soluzione più radicale è non usare web font per il testo, e affidarsi ai font di sistema di ciascun dispositivo. Il costo è nullo: nessun file da scaricare, nessuno scambio, nessuno spostamento. È la scelta di molti siti che privilegiano la velocità e di molte interfacce di applicazioni. Il compromesso è sull’identità visiva: il testo avrà un aspetto diverso su Windows, macOS, Android e iOS. Una via di mezzo diffusa è usare un web font solo per i titoli, dove l’identità conta di più e il testo è breve, e il font di sistema per il corpo del testo.
In WordPress
Nei temi a blocchi i font si dichiarano nel file theme.json o dalla libreria dei font dell’editor, e WordPress genera le regole @font-face. Per impostazione predefinita usa font-display: fallback, un comportamento intermedio con un breve periodo di testo invisibile seguito dal ripiego; il valore si può cambiare per ogni font. Il ripiego allineato, basato su font locali, si aggiunge di solito nel CSS del tema, con la sua regola @font-face, e va inserito nella lista dei font della famiglia tipografica. Nei temi classici e con i costruttori di pagine conviene verificare da dove arrivano i font: molti li caricano da servizi esterni, spesso in più famiglie e pesi, e alcuni con caricatori in JavaScript.
Come verificare
Il modo più diretto per vedere il ripiego è impedirgli di essere sostituito: nel pannello Rete dei DevTools si può bloccare la richiesta del file del font, e la pagina resta con il ripiego. Confrontando due schermate, con e senza il font bloccato, si vede subito quanto il testo cambia disposizione. Nel pannello Performance, registrando il caricamento con la rete rallentata, la traccia degli spostamenti di layout mostra se lo scambio produce spostamenti e quali elementi coinvolge; il pannello Rendering può evidenziare le aree spostate mentre la pagina si carica. Sui dati reali, un CLS che migliora dopo l’allineamento del ripiego è la conferma finale.
Un esempio concreto
Un blog aziendale usa un font con caratteri piuttosto larghi per i titoli e per il testo, servito da un servizio esterno con font-display: swap. Il rapporto delle Core Web Vitals segnala un CLS da migliorare sulle pagine degli articoli da smartphone, senza immagini o annunci che si spostino. Bloccando il font nei DevTools, il titolo principale occupa due righe invece di tre e il primo paragrafo una riga in meno: quando il font arriva, tutto il contenuto visibile scende di una sessantina di pixel. L’intervento consiste nel servire il font dal proprio dominio con un preload per il peso normale, fissare l’interlinea del testo e dei titoli, e aggiungere due ripieghi allineati, uno per Arial e uno per Roboto, calcolati con uno strumento automatico. Il confronto tra le schermate mostra ora differenze minime, e nelle settimane successive il CLS da smartphone rientra nella soglia buona.
In sintesi
I web font spostano il layout quando il font di ripiego ha metriche diverse da quelle del font del sito. La soluzione più equilibrata è mostrare subito il testo con font-display: swap e usare un ripiego allineato con size-adjust e i descrittori di override, calcolati con gli strumenti appositi e definiti per i font locali dei diversi sistemi operativi. Un’interlinea esplicita, i pesi effettivamente usati caricati come file veri, font serviti dal proprio dominio senza caricatori in JavaScript completano il lavoro. In alternativa, il font di sistema elimina il problema alla radice. In ogni caso, il risultato va verificato bloccando il font e confrontando le due versioni della pagina.