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.
pp.cul.elenco li:first-child#app .elenco > li.aula::beforeinput[type="date"]:not(.piccolo)*:is(#a, .b) p:where(#a) pa:hover#lab.occupatah2 + p- Chi vince su un elemento
<div id="x" class="a b c d e f g h i j k">:#xoppure.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:
<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):
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:
- Che colore hanno le tre voci di elenco?
- Che peso del carattere (
font-weight) hanno? - Che dimensione del carattere hanno i
li, il paragrafo e l'em? - Di che colore sarebbe "Lab Info" senza
!importantin R10? - 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 imposta0.875remsullaul:remsi riferisce all'elemento radicehtml, che qui ha la dimensione predefinita del browser (16 px), quindi 14 px. R12 imposta1.25emsulli, e l'emsi riferisce al carattere del padre: 1,25 x 14 = 17,5 px;- paragrafo: nessuna regola lo tocca, eredita da
body(font-sizesi 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:
tbody tr:nth-child(even) { background: rgb(247, 249, 251); } /* zebra */
.libera { background: lightgreen; }<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..liberavale (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: sfondilightgreen, grigio chiaro,lightgreen. - Correzione: alzare la specificità di
.liberaalmeno a (0,1,2) e scriverla dopo la regola zebra. Contbody 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 sonolightgreen. - Perché non
!important? Una volta usato, per vincere bisognerebbe usare ancora!importantcon 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.cvale (0,2,1)..sA :where(#a1)vale (0,1,0) (:whereconta 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
!importantbatta solo regole meno specifiche: batte tutte le regole normali. - Dimenticare che
colorefont-sizesi ereditano,backgroundno, e cheemsi riferisce al padre. - Leggere
.contenitore .elenco .liberacome (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; ilfont-weightnormale viene da R4 (stessa specificità di R3, scritta dopo). - Aula B2: R5
ul li.aula(0,1,2) → blu, peso bold. - Lab Info: R10
.occupatacon!important→ grigio (senza, R9#app li.occupata(1,1,1) → rosso). li= 1,25 em × 14 px (.elenco0,875 rem) = 17,5 px.- Il paragrafo eredita 16 px e
#333333dabody; l'emconcolor: inheritprende 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(). !importantche 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).