Salta al contenuto
Note per Studenti Esercizio - Calcolo della specificità e della cascata (applicazione web)

Esercizio - Calcolo della specificità e della cascata (applicazione web)

In questa pagina 4

Contesto. La pagina delle prenotazioni delle aule ha un foglio di stile che è cresciuto nel tempo, e alcune regole "non funzionano". Per capire perché si calcola, per ogni elemento, quale dichiarazione vince. Teoria: CSS3 - selettori, cascata, box model e layoutCSS descrive la presentazione: regole selettore { proprietà: valore }. Selettori: tipo, classe (.a), id (#a), attributo ([type="date"]), universale, combinatori (spazio discendente, > figlio, + fratello adiacente, ~ fratelli seguenti), pseudo-classi (:hover, :focus-visible, :nth-child, :not, :checked) e pseudo-elementi (::before, ::after). Cascata: tra più regole che toccano la stessa proprietà vince, nell'ordine, l'importanza (!important vince, e lo stile inline batte gli stili normali), poi la specificità (a,b,c) = (id, classi+attributi+pseudo-classi, tipi+pseudo-elementi) confrontata come numeri uno alla volta da sinistra, infine l'ordine di comparsa (l'ultima vince). Ereditarietà: color, font, line-height, text-align passano ai figli; margin, padding, border, background, width no; inherit, initial, unset la controllano. Box model: content, padding, border, margin; con content-box width è solo il contenuto, con border-box include padding e bordo; i margini verticali adiacenti collassano nel maggiore. display: block, inline, inline-block, none, flex, grid. position: static, relative, absolute (rispetto all'antenato posizionato), fixed, sticky. Flexbox è per una dimensione (justify-content, align-items, gap, flex-grow, flex-wrap), grid per due (grid-template-columns, fr, repeat, minmax, gap). Responsive: meta viewport, unità relative (rem, %, fr), media query @media (min-width), approccio mobile-first.CSS3 - selettori, cascata, box model e layout →; la struttura HTML è quella di HTML5 - struttura del documento, elementi semantici e formUn documento HTML5 ha la forma doctype (), html con lang, head (meta charset UTF-8, viewport, title, link, script) e body. Il parser HTML5 non dà mai errore: corregge (aggiunge il tbody, chiude i p). Gli elementi semantici (header, nav, main, section, article, aside, footer, figure, time) descrivono il ruolo del contenuto e danno ai lettori di schermo i punti di riferimento; div e span sono neutri. Link: a href (assoluto, relativo, #frammento, mailto), rel e target; immagini con alt obbligatorio. Tabelle: table, caption, thead, tbody, th con scope, td, colspan e rowspan, solo per dati. Form: action, method (GET mette i campi nella query string, per letture idempotenti e ricercabili; POST li mette nel corpo, per modificare lo stato), enctype (application/x-www-form-urlencoded predefinito, multipart/form-data per i file); si invia solo ciò che ha name; label for collega etichetta e campo; tipi di input (text, email, number, date, time, radio, checkbox, hidden...); validazione nativa con required, minlength, maxlength, min, max, step, pattern e type, aggirabile e quindi da ripetere sul server; novalidate la spegne. Accessibilità: lang, alt, label, gerarchia dei titoli, navigazione da tastiera, ARIA solo quando l'HTML nativo non basta.HTML5 - struttura del documento, elementi semantici e form →. Tutti i risultati sono stati verificati leggendo gli stili calcolati in un browser (Edge, motore Chromium).

Parte 1 - Calcolo della specificità

Calcolare la specificità (a, b, c) di ciascun selettore, con a = id, b = classi, attributi e pseudo-classi, c = tipi e pseudo-elementi.

  1. p
  2. p.c
  3. ul.elenco li:first-child
  4. #app .elenco > li.aula::before
  5. input[type="date"]:not(.piccolo)
  6. *
  7. :is(#a, .b) p
  8. :where(#a) p
  9. a:hover
  10. #lab.occupata
  11. h2 + p
  12. Chi vince su un elemento <div id="x" class="a b c d e f g h i j k">: #x oppure .a.b.c.d.e.f.g.h.i.j.k (undici classi)?

Soluzione.

n. selettore (a,b,c) conteggio
1 p (0,0,1) un tipo
2 p.c (0,1,1) una classe, un tipo
3 ul.elenco li:first-child (0,2,2) classe .elenco e pseudo-classe :first-child (b = 2); tipi ul e li (c = 2)
4 #app .elenco > li.aula::before (1,2,2) un id; classi .elenco e .aula; tipo li e pseudo-elemento ::before; il combinatore > vale 0
5 input[type="date"]:not(.piccolo) (0,2,1) l'attributo (b = 1), :not vale come il suo argomento .piccolo (b = 2), tipo input
6 * (0,0,0) l'universale non conta
7 :is(#a, .b) p (1,0,1) :is vale come l'argomento più specifico, #a; più il tipo p
8 :where(#a) p (0,0,1) :where vale sempre 0
9 a:hover (0,1,1) pseudo-classe e tipo
10 #lab.occupata (1,1,0) id e classe sullo stesso elemento (senza spazio)
11 h2 + p (0,0,2) due tipi; + vale 0
12 #x (1,0,0) contro (0,11,0) vince #x si confronta prima a: 1 > 0; la specificità non è un numero in base 10

Parte 2 - La cascata su un frammento della pagina

HTML:

html
<div id="app" class="contenitore">
  <ul class="elenco">
    <li class="aula libera">Aula A1</li>
    <li class="aula">Aula B2</li>
    <li class="aula occupata" id="lab">Lab Info</li>
  </ul>
  <p>Prenotazioni di <em>oggi</em></p>
</div>

CSS (le regole sono numerate nel commento):

css
body              { font-size: 16px; color: #333333; }           /* R1  */
li                { color: black; }                              /* R2  */
.aula             { color: green; font-weight: bold; }           /* R3  */
.libera           { font-weight: normal; }                       /* R4  */
ul li.aula        { color: blue; }                               /* R5  */
li.aula.libera    { color: teal; }                               /* R6  */
.contenitore .elenco .libera { color: orange; }                  /* R7  */
#lab              { color: purple; }                             /* R8  */
#app li.occupata  { color: red; }                                /* R9  */
.occupata         { color: gray !important; }                    /* R10 */
.elenco           { font-size: 0.875rem; }                       /* R11 */
li                { font-size: 1.25em; }                         /* R12 */
em                { color: inherit; }                            /* R13 */

Domande:

  1. Che colore hanno le tre voci di elenco?
  2. Che peso del carattere (font-weight) hanno?
  3. Che dimensione del carattere hanno i li, il paragrafo e l'em?
  4. Di che colore sarebbe "Lab Info" senza !important in R10?
  5. Di che colore è il testo "oggi" dentro l'em, e perché?

Soluzione.

Colore di "Aula A1" (classi aula e libera). Le regole di colore che la riguardano:

regola selettore specificità
R2 li (0,0,1)
R3 .aula (0,1,0)
R5 ul li.aula (0,1,2)
R6 li.aula.libera (0,2,1)
R7 .contenitore .elenco .libera (0,3,0)

La più specifica è R7 (b = 3, più delle altre): arancione. R6 e R5 perdono anche se hanno più tipi, perché prima si confronta b, e c non si guarda.

Colore di "Aula B2" (solo aula): R2, R3, R5. Vince R5 (0,1,2): blu.

Colore di "Lab Info" (aula occupata, id="lab"): R2, R3, R5, R8 (1,0,0), R9 (1,1,1) e R10. R9 batte R8 (a uguale, b = 1 contro 0), ma R10 è !important: le dichiarazioni importanti vincono su tutte quelle normali, qualunque sia la specificità: grigio.

Senza !important (domanda 4): resta R9 (1,1,1): rosso.

Peso del carattere. R3 dà bold ai tre li; R4 dà normal solo a .libera. Per "Aula A1" valgono entrambe, con la stessa specificità (0,1,0): vince la seconda nel foglio, R4: normal (400). "Aula B2" e "Lab Info": solo R3, bold (700).

Dimensioni.

  • li: R11 imposta 0.875rem sulla ul: rem si riferisce all'elemento radice html, che qui ha la dimensione predefinita del browser (16 px), quindi 14 px. R12 imposta 1.25em sul li, e l'em si riferisce al carattere del padre: 1,25 x 14 = 17,5 px;
  • paragrafo: nessuna regola lo tocca, eredita da body (font-size si eredita): 16 px;
  • em: eredita dal paragrafo: 16 px.

Colore dell'em. R13 dice color: inherit, cioè lo stesso del padre, il paragrafo. Il paragrafo non ha regole di colore (R2 vale solo per i li), ma eredita color da body: #333333. Quindi "oggi" è #333333 (rgb 51, 51, 51). Nel browser: voci di elenco arancione, blu, grigio (rosso senza !important), peso 400, 700, 700, dimensione 17,5 px; paragrafo ed em rgb(51, 51, 51) e 16 px.

Parte 3 - Una regola che non si applica

Nella tabella delle prenotazioni le righe libere devono avere sfondo verde chiaro:

css
tbody tr:nth-child(even) { background: rgb(247, 249, 251); }    /* zebra */
.libera                  { background: lightgreen; }
html
<table><tbody>
  <tr class="libera"><td>Aula A1</td></tr>
  <tr class="libera"><td>Aula B2</td></tr>
  <tr class="libera"><td>Lab Info</td></tr>
</tbody></table>

Le righe 1 e 3 sono verdi, la riga 2 no. Perché? Correggere senza !important.

Soluzione.

  • La riga 2 è pari e libera: due regole competono per background. tbody tr:nth-child(even) vale (0,1,2): una pseudo-classe, due tipi. .libera vale (0,1,0). Vince la prima (0,1,2) > (0,1,0): c è 2 contro 0. Nelle righe dispari la prima regola non si applica e vince .libera. Verificato: sfondi lightgreen, grigio chiaro, lightgreen.
  • Correzione: alzare la specificità di .libera almeno a (0,1,2) e scriverla dopo la regola zebra. Con tbody tr.libera { background: lightgreen; } la specificità è (0,1,2): pari a quella della regola zebra, e a parità vince la regola scritta per ultima. Verificato: le tre righe sono lightgreen.
  • Perché non !important? Una volta usato, per vincere bisognerebbe usare ancora !important con selettori più specifici: il foglio diventa ingestibile. Meglio selettori simili per specificità e ordinati dal più generale al più particolare.

Parte 4 - Raggruppare con :not, :is, :where

Dire il colore del paragrafo (ognuno è in un div con la classe indicata nella prima colonna) e perché.

caso HTML regole
A <div class="sA"><p id="a1" class="c"> .sA p.c { color: blue; } poi .sA :where(#a1) { color: red; }
B <div class="sB"><p id="b1" class="c"> .sB p.c { color: blue; } poi .sB p:not(.zz) { color: orange; }
C <div class="sC"><p id="c1" class="c"> .sC p.c { color: blue; } poi .sC :is(#c1, .zz) { color: green; }

Soluzione.

  • A: blu. .sA p.c vale (0,2,1). .sA :where(#a1) vale (0,1,0) (:where conta 0, resta solo .sA), anche se è scritta dopo. Perde per specificità.
  • B: arancione. .sB p.c è (0,2,1); .sB p:not(.zz) è (0,2,1) (:not(.zz) vale come .zz, quindi b = 2, più p). Specificità uguale: vince la seconda perché scritta dopo.
  • C: verde. :is(#c1, .zz) vale come l'argomento più specifico, #c1: (1,0,0); con .sC è (1,1,0) e batte (0,2,1) a prescindere dall'ordine.

Verificato: blu, arancione, verde.

Errori tipici

  • Trattare (0,11,0) come 11 e confrontarlo con (1,0,0) come 1: non è un numero in base 10.
  • Contare i combinatori (>, +, spazio) o * nella specificità.
  • Dimenticare che a parità di specificità conta l'ordine nel foglio, e che :where() vale 0.
  • Pensare che !important batta solo regole meno specifiche: batte tutte le regole normali.
  • Dimenticare che color e font-size si ereditano, background no, e che em si riferisce al padre.
  • Leggere .contenitore .elenco .libera come (0,1,2) perché ha "più parti": sono tre classi, (0,3,0).

Versione ripasso

Specificità (a,b,c). a = id; b = classi, attributi, pseudo-classi; c = tipi, pseudo-elementi. * e combinatori valgono 0; :where() vale 0; :is() e :not() valgono l'argomento più specifico. Confronto da sinistra, cifra per cifra (CSS3 - selettori, cascata, box model e layoutCSS descrive la presentazione: regole selettore { proprietà: valore }. Selettori: tipo, classe (.a), id (#a), attributo ([type="date"]), universale, combinatori (spazio discendente, > figlio, + fratello adiacente, ~ fratelli seguenti), pseudo-classi (:hover, :focus-visible, :nth-child, :not, :checked) e pseudo-elementi (::before, ::after). Cascata: tra più regole che toccano la stessa proprietà vince, nell'ordine, l'importanza (!important vince, e lo stile inline batte gli stili normali), poi la specificità (a,b,c) = (id, classi+attributi+pseudo-classi, tipi+pseudo-elementi) confrontata come numeri uno alla volta da sinistra, infine l'ordine di comparsa (l'ultima vince). Ereditarietà: color, font, line-height, text-align passano ai figli; margin, padding, border, background, width no; inherit, initial, unset la controllano. Box model: content, padding, border, margin; con content-box width è solo il contenuto, con border-box include padding e bordo; i margini verticali adiacenti collassano nel maggiore. display: block, inline, inline-block, none, flex, grid. position: static, relative, absolute (rispetto all'antenato posizionato), fixed, sticky. Flexbox è per una dimensione (justify-content, align-items, gap, flex-grow, flex-wrap), grid per due (grid-template-columns, fr, repeat, minmax, gap). Responsive: meta viewport, unità relative (rem, %, fr), media query @media (min-width), approccio mobile-first.CSS3 - selettori, cascata, box model e layout →).

Esempi.

  • ul.elenco li:first-child (0,2,2).
  • #app .elenco > li.aula::before (1,2,2).
  • input[type="date"]:not(.piccolo) (0,2,1).
  • :is(#a, .b) p (1,0,1); :where(#a) p (0,0,1).
  • #x (1,0,0) batte undici classi (0,11,0).

Cascata: importanza (!important, poi inline) → specificità → ordine (l'ultima vince).

Frammento delle aule.

  • Aula A1: R7 .contenitore .elenco .libera (0,3,0) → arancione; il font-weight normale viene da R4 (stessa specificità di R3, scritta dopo).
  • Aula B2: R5 ul li.aula (0,1,2) → blu, peso bold.
  • Lab Info: R10 .occupata con !important → grigio (senza, R9 #app li.occupata (1,1,1) → rosso).
  • li = 1,25 em × 14 px (.elenco 0,875 rem) = 17,5 px.
  • Il paragrafo eredita 16 px e #333333 da body; l'em con color: inherit prende lo stesso colore.

Regola che non si applica. tbody tr:nth-child(even) (0,1,2) batte .libera (0,1,0) sulle righe pari. Correzione senza !important: tbody tr.libera (0,1,2) scritta dopo.

Raggruppare. A: :where(#a1) vale 0, vince p.c (blu). B: p:not(.zz) pareggia p.c, vince l'ultima (arancione). C: :is(#c1, .zz) vale come #c1 (verde).

Errori tipici

  • Specificità come numero in base 10.
  • Contare combinatori e *.
  • Dimenticare l'ordine a parità e il valore zero di :where().
  • !important che batte tutto, non solo le regole meno specifiche.
  • Confondere proprietà ereditate e non ereditate. Soluzione di sintesi.
selettore (a,b,c)
p.c (0,1,1)
a:hover (0,1,1)
h2 + p (0,0,2)
#lab.occupata (1,1,0)
* (0,0,0)
:is(#a, .b) p (1,0,1)

Colori del frammento.

voce regole in gioco vince
Aula A1 (aula libera) R2, R3, R5, R6, R7 R7 (0,3,0): arancione
Aula B2 (aula) R2, R3, R5 R5 (0,1,2): blu
Lab Info (aula occupata, #lab) R2, R3, R5, R8, R9, R10 R10 !important: grigio

Senza !important vince R9 (1,1,1): rosso. Peso del carattere: R3 bold, R4 normal solo per A1 (stessa specificità, R4 scritta dopo).

Dimensioni. li: 1,25 em del padre .elenco a 0,875 rem = 14 px, cioè 17,5 px; paragrafo ed em ereditano 16 px e #333333 da body.

Procedura. (1) trova le regole che toccano la proprietà; (2) c'è un !important? vince; (3) altrimenti confronta le terne da sinistra; (4) a parità, l'ultima scritta; (5) se nessuna regola mira all'elemento, eredita (se la proprietà è ereditata).

Teoria collegata