Frontend

Come il browser elabora CSS

Autore

Manuel Ricci

Questa prima lezione è tosta, molto tosta. Lo dico perché per qualcuno che inizia con CSS potrebbe quasi scoraggiare dall’iniziare, ma giuro che sono concetti molto utili da conoscere per poter scrivere meglio il CSS di tutti i giorni.

Che cos’è CSS?

Cascading Style Sheets (CSS) è un linguaggio di stile usando per descrivere la presentazione di un documento HTML o XML (inclusi dialetti di XML come SVG).

CSS descrive come gli elementi devono essere renderizzati a schermo ed è proprio su questo processo che ci concentreremo nell’arco di questo approfondimento.

CSS fa parte dei linguaggi core del web ed è standardizzato tra i vari browser in accordo con la specifica W3C.

Per quanto CSS sia noto a molti come CSS3, il che indica che ci siano altre versioni (per la precisione la 1 e la 2.1) ad oggi il versionamento non esiste più e ciò significa che non ci sarà mai CSS4, ma solo CSS.

Questa scelta è dovuta alla dimensione della specifica e alla nascita di sempre più moduli di CSS, che tra loro differiscono parecchio, per cui è diventato più utile sviluppare i moduli separatamente. Al posto di avere una versione generale, la W3C, periodicamente fa un’istantanea della specifica e il progresso individuale dei moduli. A quest’ultimi viene applicato poi un livello (es. CSS Color Module Level 5).

Come fa il browser a comprendere CSS?

Questa domanda ce la siamo fatta anche con HTML ed è proprio da lì che dobbiamo partire, da quei processi visti nel corso di HTML. In particolar modo il pre-parsing della ben più complessa fase di parsing che il rendering engine esegue sul documento HTML.

Durante suddetta sottofase tutto il CSS (sia quello inserito via attributo style, incluso con il tag <style> o scritto in un documento separato e linkato con il tag <link />) viene raccolto e mandato al parser di CSS.

Come per HTML anche per CSS c’è una fase di tokenization la quale trasforma ciò che è stato scritto nel documento in token, i quali si basano sulla specifica W3C menzionata in precedenza.

Al termine del processo otterremo una struttura dati con tutti i selettori, proprietà e i rispettivi valori di quest’ultime.

Facciamo un esempio

1.bottone {
2   background: green;
3   border: 3px solid red;
4   font-size: 1em;
5}

Ignorando per il momento cosa fanno esattamente queste righe di codice, concentriamoci sull’output del parser che sarà più o meno simile a quanto segue:

Selettore Proprietà Valore
.bottone background-color rgb(0,255,0)
.bottone border-width 3px
.bottone border-style solid
.bottone border-color rgb(255,0,0)
.bottone font-size 1em

Unica cosa da notare, ma che avrà più senso con qualche nozione generale in più sulle spalle è che il browser non considera le shorthand (es. border e background), ma solo le proprietà estese (o longhand).

Dopo la parsificazione il rendering engine continua la sua attività di costruzione del DOM.

Computazione

Ovviamente non è finita qui, siamo ben lontani dalla fine. A questo punto inizia un nuovo processo denominato computazione, dove tutti i valori vengono convertiti nel loro valore computazionale standard.

Al termine di questo processo tutti i valori avranno uno dei tre possibili output:

  • auto
  • una percentuale
  • valore in pixel

Chiaro che nel caso di colori o altri tipi di valori testuali questi output non si applicano.

Per fare qualche esempio:

Input Valore computato
font-size: 1rem font-size: 16px
width: 50% width: 50%
line-width: calc(10px + 2em) line-width: 42px
border-color: currentColor border-color: rgb(0, 0, 0)
height: 50vh height: 540px
display: grid display: grid

Le varie unità di misura, funzioni e diciture verranno affrontate più avanti nel corso. Per ora basta solo notare che valori come 1rem o 50vh cambiano in un valore in pixel.

Applicazione delle regole di specificità e cascata

Questa nuova fase è estremamente importante, questo perché il browser deve determinare quali stili applicare esattamente ad un determinato elemento.

La scelta si basa su una formula chiamata specificità, la quale conta il numero di tag, classi, id e attributi presenti nel selettore utilizzato e il numero di !important che vengono usati.

Se gli stili sono applicati attraverso l’attributo style, questo ha automaticamente la specificità più alta rispetto al CSS nel tag <style> o in un documento esterno. Se usiamo !important su un valore specifico questo vincerà su tutto il resto ignorando selettori o dove è stato scritto.

Il punteggio quindi può essere facilmente calcolato prendendo come riferimento questa semplice tabellina e aggiungendo un uno ogni volta che compare nel selettore un tipo specifico.

calcolo della specificità in CSS

Qui nelle tabella che segue qualche semplice esempio:

Selettore Specificità
p 0 0 0 0 1
p.intro 0 0 0 1 1
#header nav ul li.active 0 0 1 1 3
<p style=”color: #bada55”> 0 1 0 0 0
color: #bada55 !important; 1 0 0 0 0

Durante la lezione sui selettori ne vedremo tanti altri e analizzeremo la specificità di ognuno.

Cosa succede in caso di pareggio?

Nel caso due selettori di pari specificità applichino degli stili allo stesso elemento quello che prevale è quello che viene dopo. Questa è la seconda regola fondamentale di CSS.

Prendendo in considerazione il primo esempio:

1.bottone {
2   background: green;
3   border: 3px solid red;
4   font-size: 1em;
5}
6
7div .bottone {
8   background: yellow;
9}

La nostra tabella aggiornata e arricchita sarà la seguente:

SelettoreProprietàValoreSpecificitàOrdine
.bottone background-color rgb(0,255,0) 0 0 0 1 0 0
.bottone border-width 3px 0 0 0 1 0 1
.bottone border-style solid 0 0 0 1 0 2
.bottone border-color rgb(255,0,0) 0 0 0 1 0 3
.bottone font-size 16px 0 0 0 1 0 4
div .bottone background-color rgb(255, 255, 0) 0 0 0 1 1 5

L’origine dello stile

In CSS gli stili hanno tre origini:

  1. utente: qualsiasi stile applicato globalmente tramite user agent dall’utente (es. dimensione del testo più grande)
  2. autore: gli stili scritti dal frontend developer
  3. user agent: gli stili di base del browser

L’ordine d’importanza è quello del listato: prima l’utente, poi l’autore e infine lo user agent.

Ipotizzando che l’utente abbia modificato la dimensione del testo a 32px con le nuove informazioni acquisite possiamo arricchire ulteriormente la nostra tabella:

OrigineSelettoreProprietàValoreSpecificitàOrdine
Autore .bottone background-color rgb(0,255,0) 0 0 0 1 0 0
Autore .bottone border-width 3px 0 0 0 1 0 1
Autore .bottone border-style solid 0 0 0 1 0 2
Autore .bottone border-color rgb(255,0,0) 0 0 0 1 0 3
Autore .bottone font-size 16px 0 0 0 1 0 4
Autore div .bottone background-color rgb(255, 255, 0) 0 0 0 1 1 5
Utente * font-size 32px 0 0 0 0 1 0

Infine il browser ordinerà la struttura dati prima per origine, poi per specificità e infine per ordine.

Origine ⬆️SelettoreProprietàValoreSpecificità ⬆️Ordine ⬇️
Utente * font-size 32px 0 0 0 0 1 0
Autore div .bottone background-color rgb(255, 255, 0) 0 0 0 1 1 5
Autore .bottone background-color rgb(0,255,0) 0 0 0 1 0 0
Autore .bottone border-width 3px 0 0 0 1 0 1
Autore .bottone border-style solid 0 0 0 1 0 2
Autore .bottone border-color rgb(255,0,0) 0 0 0 1 0 3
Autore .bottone font-size 16px 0 0 0 1 0 4

A questo punto abbiamo le proprietà vincenti per il nostro elemento con classe .bottone (più in alto meglio è). Gli stili applicati al browser saranno quindi:

Proprietà Valore
font-size 32px
background-color rgb(255, 255, 0)
border-width 3px
border-color rgb(255, 0, 0)
border-style solid

Se quanto hai letto non ti basta puoi sempre leggere la specifica ufficiale della W3C.

Giunti a questo punto è il momento di aggiornare il CSSOM (CSS Object Model), il quale risiede in document.stylesheets alla quale si accede solitamente con il metodo getComputedStyle() usando JavaScript.

Layout e rappresentazione visuale

Il browser a questo punto creerà il box tree. Per crearlo è necessario attraversare tutto il DOM e creare zero o più box CSS, dove ognuno ha margini, bordi, padding e contenuto.

Questo processo si chiama layout.

Questo processo in particolare è assai complesso e capisco che affrontarlo ora sarebbe veramente too much. Per cui il processo di layout verrà discusso di volta in volta che verranno introdotte le proprietà che vengono risolte in questa fase.

Painting

Facciamo un breve recap di quanto visto finora:

  1. Il browser recupera tutto il CSS
  2. Lo parsifica e ci applica cascata e specificità
  3. Aggiorna il CSSOM
  4. Creare il box tree e risolve le proprietà di layout come float, dimensioni auto e frammentazioni

Cosa manca? Beh non abbiamo ancora applicato colori, bordi, ombre e altri elementi di design. Questo processo è chiamato painting.

L’ordine che si segue in questa fase è: background, bordi e contenuto, il che visivamente si traduce così:

processo di painting in CSS

Una volta terminato il tutto verrà convertito in bitmap. Eh sì, hai letto bene. Un’immagine.

null

Lo stacking context

Anche questa fase la vedremo meglio quando parlerò di position, ma è solo a questo punto che vengono gestiti gli z-index una proprietà che ci permette di mostrare un elemento sopra un altro. Un po’ come i livelli di Photoshop, solo che in questo punto specifico ancora non si parla di livelli (layer). Qui si chiama stacking context e con z-index lo modifichiamo.

Composition

Ora iniziamo a parlare di layer perché quanto prodotto finora viene passato ad un software rasterizer che creerà vari livelli e li renderizzerà a video come bitmap.

Di layer e software rasterizer ne ho parlato ampiamente nel corso one shot sull’ottimizzazione delle animazioni in CSS.

Il motivo della creazione dei livelli e quindi lo spezzettamento della pagina web in tante piccole “piastrelle” è per questioni legate all’aggiornamento della pagina in caso di animazioni o cambiamenti in generale del layout.

Il senso di interattività

La pagina è pronta, l’utente può navigarla e interagirci e proprio su quest’ultimo aspetto voglio concentrarmi.

Cosa accade quando un utente passa il mouse su un bottone e questo cambia colore? Il processo è abbastanza semplice e coinvolge un algoritmo chiamato hit testing.

  1. L’utente muove il mouse sul bottone
  2. Il browser esegue un evento che comunica che il mouse si sta spostando ed esegue l’algoritmo di hit testing che in sostanza chiede “Quale box sta toccando il mouse?”
  3. L’algoritmo restituisce il box (il bottone)
  4. Il browser chiede “C’è qualcosa da fare quando il mouse passa sopra il bottone?”
  5. Viene eseguita al volo la parte di stile e cascata vista in precedenza solo per questo box e i suoi figli per determinare che c’è qualcosa e vengono passati solo gli stili da modificare
  6. Gli stili vengono appesi all’elemento DOM (il bottone)
  7. Viene bypassata la parte di layout e si passa immediatamente alla fase di painting creando un nuovo bitmap
  8. Il nuovo bitmap viene passato al compositor e quindi fatto vedere all’utente.

Per l’utente si tratta di interazione, ma per il browser si tratta semplicemente di scambiare un’immagine con un’altra.

Un viaggio bello lungo

Come già scritto il processo non è ancora completo, mancano delle nozioni interessanti, ma che possiamo affrontare di volta in volta che ci si presenta l’occasione. Per ora fai sedimentare questi concetti che sono già più che sufficienti.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.