Progettazione Web - CSS
Cerca per parola chiave 0/0

CSS

Selettori e cascata, colori e testo, box model, Flexbox e Grid, design responsive e animazioni

L'HTML dice che cosa è ogni parte della pagina; il CSS dice come deve apparire e dove si colloca. Il modulo segue un ordine preciso: prima come si scrivono le regole e come si scelgono gli elementi, poi quale regola vince quando se ne contendono lo stesso elemento, infine lo spazio: dimensioni, margini, impaginazione con Flexbox e Grid, adattamento agli schermi. Come sempre, gli esempi si possono modificare.

Che cos'è il CSS

Un linguaggio per l'aspetto

CSS (Cascading Style Sheets, «fogli di stile a cascata») è lo standard del W3C per descrivere la presentazione degli elementi HTML: caratteri, colori, dimensioni, bordi, immagini di sfondo, spaziature e la posizione stessa degli elementi nella pagina. È un linguaggio con una sua sintassi, diversa da quella dell'HTML.

Guarda la stessa identica pagina HTML con due fogli di stile diversi: nella scheda CSS puoi cambiare tutto senza toccare l'HTML.

Separare l'aspetto dal contenuto ha molti vantaggi:

  • Più controllo: il CSS offre molte più possibilità di formattazione di quante ne abbia mai avute l'HTML.
  • Manutenzione: tutta la formattazione del sito sta in uno o pochi file. Cambi un colore in un punto, e cambia su tutte le pagine.
  • Accessibilità: le pagine restano HTML semantico pulito, e l'utente può applicare i propri stili (caratteri più grandi, alto contrasto).
  • Velocità: ogni pagina HTML è più leggera, e il file CSS, scaricato una volta, resta nella cache del browser.
  • Più dispositivi: lo stesso documento si adatta allo schermo di un telefono, a un monitor, alla stampa.

Per anni il CSS ha avuto anche un difetto noto: non era nato come linguaggio per l'impaginazione, e costruire colonne e layout richiedeva trucchi con elementi flottanti, posizionamenti relativi, altezze incoerenti. Oggi il problema è risolto da Flexbox e Grid.

Le versioni

VersioneDataNovità
CSS livello 117 dicembre 1996caratteri, colori, testo, margini e bordi
CSS livello 212 maggio 1998posizionamento, tipi di media, selettori più potenti
CSS 2.17 giugno 2011revisione di CSS2 che corregge gli errori e registra ciò che i browser implementano davvero
«CSS3» e oltredal 2011il linguaggio viene diviso in moduli indipendenti (colori, selettori, Flexbox, Grid…), ognuno con il suo livello

Non esiste quindi un «CSS3» o un «CSS4» unico: ogni modulo evolve per conto suo, e il livello di un modulo non indica la versione del CSS nel suo insieme. Il W3C pubblica periodicamente uno snapshot che raccoglie le specifiche stabili del momento, come lo CSS Snapshot 2026.

La sintassi

Regole, selettori e dichiarazioni

Un foglio di stile è una sequenza di regole. Una regola è fatta di un selettore, che dice a quali elementi si applica, seguito da un blocco di dichiarazioni tra parentesi graffe. Ogni dichiarazione è una coppia proprietà: valore, terminata da un punto e virgola.

p { color: red; font-size: 1.2em; } blocco di dichiarazioni dichiarazione selettore proprietàvalore il punto e virgola chiudeogni dichiarazione
Una regola completa: il selettore sceglie gli elementi, le dichiarazioni dicono cosa cambiare.
  • Il selettore è uno schema che il browser usa per trovare gli elementi HTML a cui applicare la regola.
  • Le proprietà hanno nomi predefiniti dallo standard: già CSS 2.1 ne definiva più di cento, oggi sono diverse centinaia.
  • Il browser ignora gli spazi: un blocco può stare su una riga sola o su molte. Di solito si scrive una dichiarazione per riga.
  • Il punto e virgola dopo l'ultima dichiarazione è facoltativo, ma metterlo sempre evita errori quando se ne aggiunge un'altra.
  • Un errore in una dichiarazione fa ignorare solo quella: il browser salta ciò che non capisce e prosegue.
em { color: red; }

p {
  margin: 5px 0 10px 0;
  font-weight: bold;
  font-family: Arial, Helvetica, sans-serif;
}

In CSS i commenti si scrivono tra /* e */ e possono stare su più righe. Non esiste un commento «fino a fine riga» come // in altri linguaggi.

/* Colori principali del sito */
h1 { color: #9a3412; }  /* arancio scuro */

Proprietà e valori

CategoriaProprietà
Caratterifont, font-family, font-size, font-style, font-weight, @font-face
Testoletter-spacing, line-height, text-align, text-decoration, text-indent, text-transform, text-shadow
Colore e sfondocolor, background, background-color, background-image, background-position, background-repeat, box-shadow, opacity
Bordiborder, border-color, border-width, border-style, border-top… border-radius, border-image
Spaziaturepadding, padding-top…, margin, margin-top…
Dimensioniwidth, height, min-width, max-width, min-height, max-height
Impaginazionedisplay, position, top, right, bottom, left, float, clear, overflow, visibility, z-index, gap
Listelist-style, list-style-type, list-style-image
Effettitransform, transition, animation, filter, perspective

Ogni proprietà accetta certi tipi di valore:

  • parole chiave predefinite: bold, center, none, auto, solid;
  • lunghezze, con la loro unità: 16px, 1.5em, 2rem, 50vw (tra il numero e l'unità non ci vanno spazi; per lo zero l'unità si può omettere);
  • percentuali: 50%, relative a qualcos'altro che dipende dalla proprietà;
  • numeri senza unità: line-height: 1.5, opacity: .8, font-weight: 700;
  • colori: red, #c2410c, rgb(194 65 12);
  • URL: url("sfondo.png"), relativo alla posizione del file CSS, non della pagina HTML;
  • funzioni: calc(100% - 40px), linear-gradient(…), var(--colore).

Dove si scrivono gli stili

Tre fogli di stile, tre posti

Su ogni pagina agiscono in realtà tre tipi di fogli di stile:

  1. il foglio del browser (user agent stylesheet), con gli stili predefiniti di ogni elemento: è per questo che un h1 senza CSS è grande e in grassetto;
  2. il foglio dell'utente: nelle opzioni di accessibilità del browser, chiunque può impostare caratteri o colori preferiti;
  3. i fogli dell'autore, cioè quelli che scrivi tu.

Gli stili dell'autore si possono mettere in tre posti diversi, anche combinati.

Le regole si scrivono direttamente su un elemento, nell'attributo style, senza selettore né graffe:

Gli stili in linea sono sconsigliati: mescolano di nuovo aspetto e contenuto, vanno ripetuti su ogni elemento e sono difficilissimi da sovrascrivere. Qui, per cambiare la dimensione di tutti gli h2, bisogna modificarli uno per uno.

Foglio incorporato ed esterno

Le regole si scrivono in un elemento style dentro la testa del documento. È meglio degli stili in linea, ma ogni pagina ha il suo style: con più pagine diventa difficile mantenerle coerenti.

<head>
  <style>
    h1 { font-size: 24pt; }
    h2 { font-size: 18pt; font-weight: bold; }
  </style>
</head>

Le regole stanno in un file di testo separato, con estensione .css, collegato alla pagina con l'elemento link. È di gran lunga la soluzione più usata:

  • quando modifichi il foglio esterno, tutte le pagine che lo collegano cambiano automaticamente;
  • il browser lo tiene in cache: si scarica una volta per tutto il sito.
/* esempio.css — un foglio di stile esterno, collegato con <link> */
body { font-family: Georgia, "Times New Roman", serif; background: #fffaf3; }
h1 { color: #9a3412; border-bottom: 2px solid #fdba74; padding-bottom: .2em; }
p { line-height: 1.6; }

I selettori

Elementi, gruppi e universale

Il selettore più semplice è il nome di un elemento: p seleziona tutti i paragrafi, h1 tutti i titoli di primo livello. L'asterisco * è il selettore universale e seleziona tutti gli elementi.

Più selettori separati da virgole condividono lo stesso blocco di dichiarazioni. È un modo semplice per ridurre dimensioni e complessità del CSS, unendo regole identiche.

Ripetuto
h1 { color: #9a3412; font-family: Georgia, serif; }
h2 { color: #9a3412; font-family: Georgia, serif; }
h3 { color: #9a3412; font-family: Georgia, serif; }
Raggruppato
h1, h2, h3 {
  color: #9a3412;
  font-family: Georgia, serif;
}

Selettori di classe

Un selettore di classe colpisce contemporaneamente elementi HTML diversi, ovunque si trovino nel documento, purché abbiano lo stesso valore nell'attributo class. Si scrive con un punto seguito dal nome della classe. Si può anche combinare con un elemento: p.first seleziona solo i paragrafi con classe first.

Selettori di id

Un selettore di id colpisce un elemento preciso, attraverso il suo attributo id, indipendentemente dal tipo e dalla posizione. Si scrive con un cancelletto # seguito dal nome.

Id o classe? Il selettore di id si usa solo per un singolo elemento, perché un id può comparire una volta sola per pagina. Le classi si usano per gruppi (anche potenziali) di elementi correlati. Nella pratica moderna si usano quasi sempre le classi, anche per un solo elemento: un selettore di id è così «forte» (lo vedrai con la specificità) da rendere difficile sovrascriverlo.

Selettori di attributo

Selezionano gli elementi in base alla presenza di un attributo o al suo valore, tra parentesi quadre.

SelettoreSeleziona gli elementi…Esempio
[attr]che hanno l'attributo, con qualsiasi valorea[title]
[attr="v"]con valore esattamente va[href="https://example.org"]
[attr~="v"]il cui valore è un elenco di parole separate da spazi, una delle quali è esattamente va[class~="logo"]
[attr^="v"]il cui valore inizia con va[href^="https"]
[attr$="v"]il cui valore finisce con va[href$=".pdf"]
[attr*="v"]il cui valore contiene va[href*="example"]
[attr|="v"]con valore v o che inizia con v- (utile per le lingue)[lang|="en"]

Le pseudo-classi

Una pseudo-classe seleziona elementi veri, ma in base a uno stato (il mouse ci passa sopra, il campo ha il fuoco, il link è già stato visitato) o alla loro posizione nell'albero (primo figlio, ultimo figlio, dispari). Si scrive con due punti: a:hover.

Pseudo-classeSeleziona
:link, :visitedlink non ancora visitati / già visitati
:hoverl'elemento sotto il puntatore del mouse
:activel'elemento mentre viene premuto
:focus, :focus-visiblel'elemento che ha il fuoco (il secondo solo quando il fuoco va mostrato, per esempio da tastiera)
:checked, :disabled, :required, :valid, :invalidstati dei campi dei moduli
:first-child, :last-child, :only-childprimo, ultimo, unico figlio del suo genitore
:nth-child(n)per posizione: :nth-child(2), :nth-child(odd) (dispari), :nth-child(3n) (uno ogni tre)
:first-of-type, :nth-of-type(n)come sopra, ma contando solo gli elementi dello stesso tipo
:not(s)gli elementi che non corrispondono a s
:is(s1, s2), :where(s1, s2)uno qualsiasi dei selettori elencati (:where con specificità zero)
:has(s)gli elementi che contengono qualcosa che corrisponde a s: il «selettore genitore» atteso per vent'anni
:emptygli elementi senza figli né testo
:targetl'elemento il cui id è nel frammento dell'URL (#…)

Per i link l'ordine delle regole conta: si scrive :link, :visited, :hover, :active (in inglese si ricorda con «LoVe HAte»), altrimenti una regola successiva annulla la precedente.

L'ultima regola usa :has(): colora l'etichetta quando contiene una casella spuntata. Prima di :has() serviva JavaScript.

Gli pseudo-elementi

Uno pseudo-elemento seleziona qualcosa che non esiste come elemento nell'albero del documento, ma che è comunque riconoscibile: la prima lettera di un paragrafo, la prima riga, un contenuto aggiunto prima o dopo. Si scrive con due volte i due punti: p::first-line. I browser accettano ancora i due punti singoli per i quattro più vecchi (:before, :after, :first-letter, :first-line), per compatibilità.

Pseudo-elementoSeleziona
::before, ::afterun elemento virtuale creato come primo o ultimo figlio, con il contenuto della proprietà content
::first-letterla prima lettera di un blocco: i capilettera
::first-linela prima riga di un blocco, qualunque sia la larghezza
::markeril pallino o il numero delle voci di lista
::placeholderil testo segnaposto di un campo
::selectionil testo selezionato dall'utente

Il testo aggiunto con ::before e ::after non fa parte del documento: non si può selezionare e molti lettori di schermo lo ignorano. Va usato per decorazioni, non per contenuti importanti.

Selettori contestuali (combinatori)

Un selettore contestuale sceglie gli elementi in base al loro contesto: gli antenati, i figli, i fratelli. Si ottiene combinando due selettori con un combinatore.

CombinatoreNomeEsempioSeleziona
spaziodiscendentediv pi p che stanno ovunque dentro un div, a qualsiasi profondità
>figliodiv > h2gli h2 figli diretti di un div
+fratello adiacenteh3 + pil p che viene subito dopo un h3, con lo stesso genitore
~fratello genericoh3 ~ ptutti i p che vengono dopo un h3, con lo stesso genitore

Il selettore discendente è di gran lunga il più usato. Si possono concatenare quanti selettori si vuole: #main div p:first-child seleziona i p che sono primo figlio del loro genitore e che stanno dentro un div, a sua volta dentro l'elemento con id="main". I selettori si leggono da destra a sinistra: l'ultimo dice cosa si seleziona, gli altri dove deve trovarsi.

Mettili alla prova nel laboratorio: scrivi un selettore, o scegline uno, e guarda quali elementi si illuminano nella pagina.

La cascata

Perché nascono i conflitti

Ci sono tre tipi di fogli di stile (del browser, dell'utente, dell'autore), e un autore può scrivere più regole per lo stesso elemento. Quando due regole assegnano valori diversi alla stessa proprietà dello stesso elemento, quale vince? Il CSS risolve i conflitti con un sistema preciso, la cascata (è la «C» del nome), basato su tre principi:

  1. ereditarietà;
  2. specificità;
  3. posizione.

1. L'ereditarietà

Molte proprietà, ma non tutte, si applicano non solo all'elemento ma anche a tutti i suoi discendenti, che le «ereditano».

Si ereditano

Le proprietà di testo e caratteri: color, font-family, font-size, font-weight, line-height, text-align, letter-spacing, list-style, visibility, cursor.

Non si ereditano

Le proprietà di impaginazione, dimensioni, bordi, sfondi e spaziature: margin, padding, border, background, width, height, display, position.

Ha senso: se scegli un carattere per body, vuoi che valga per tutta la pagina; se metti un bordo a body, non vuoi un bordo attorno a ogni paragrafo.

Tutto il testo è rosso e in Arial, ma il bordo verde c'è solo attorno al body. Il titolo h1 resta grande e in grassetto: quelle proprietà gliele dà il foglio di stile del browser, che vince sull'ereditarietà.

Si può forzare l'ereditarietà di una proprietà che normalmente non si eredita con il valore inherit. Altre parole chiave valgono per qualsiasi proprietà: initial (il valore iniziale definito dallo standard), unset (si eredita se è ereditabile, altrimenti torna iniziale) e revert (torna allo stile del browser).

Ora i paragrafi prendono dal div anche bordo e margine. Prova a togliere la regola su p.

2. La specificità

La specificità è il modo in cui il browser decide quale regola prevale quando più regole si applicano allo stesso elemento. Più il selettore è specifico, più pesa: il browser assegna un peso a ogni regola, e vince quella con il peso maggiore. In breve: un selettore di elemento è battuto da uno di classe, che è battuto da uno di id, che è battuto da uno stile in linea.

Il testo sciolto nel body è rosso; quello sciolto nel div è magenta (le regole su div sono più specifiche di quelle ereditate dal body); i paragrafi sono verdi; .last batte p e rende blu i paragrafi di Dave; #verylast batte .last e rende arancione l'ultimo.

Il calcolo esatto funziona così. Per ogni selettore si contano tre numeri, e se la dichiarazione viene da un attributo style se ne aggiunge un quarto davanti:

  1. a: 1 se la dichiarazione è in un attributo style, altrimenti 0;
  2. b: il numero di id nel selettore;
  3. c: il numero di classi, attributi e pseudo-classi;
  4. d: il numero di elementi e pseudo-elementi.

I quattro numeri si confrontano da sinistra a destra, come le cifre di un numero ma senza riporto: vince chi ha il valore maggiore nella prima colonna in cui c'è differenza. Un solo id batte qualsiasi quantità di classi; una sola classe batte qualsiasi quantità di elementi. Il selettore universale * e i combinatori non contano nulla.

Selettoreab (id)c (classi, attributi, pseudo-classi)d (elementi)
p0001
div p0002
.last0010
p.last:hover0021
a[href$=".pdf"]::after0012
#verylast0100
#main div p:first-child0112
style="color: red"1000

Due casi moderni: :is(), :not() e :has() valgono quanto il selettore più specifico tra quelli che contengono; :where() vale sempre zero, ed è utile per scrivere stili di base facili da sovrascrivere.

3. La posizione e !important

Quando ereditarietà e specificità non bastano a decidere, cioè quando due regole hanno la stessa specificità, entra in gioco la posizione: vince la regola scritta per ultima. Conta l'ordine in cui il browser incontra le regole: prima i fogli collegati e incorporati nell'ordine in cui compaiono nella testa, e dentro ciascuno dall'alto in basso. Gli stili in linea, avendo specificità maggiore, vincono comunque.

È rosso: lo stile in linea batte tutto. Togli l'attributo style e diventa magenta (nel blocco style, l'ultima dichiarazione vince sulla prima); togli anche il blocco style e diventa blu (stylesWW.css viene dopo stylesA.css); togli il secondo link e diventa verde.

C'è un'eccezione al principio di posizione. Una dichiarazione marcata !important in un foglio dell'autore prevale su qualsiasi altra dichiarazione dell'autore, indipendentemente da posizione e specificità, anche su uno stile in linea. L'unica cosa che batte un !important dell'autore è un !important nel foglio dell'utente: così chi ha bisogno di caratteri grandi o di alto contrasto ha sempre l'ultima parola.

p {
  color: gray;                /* dichiarazione normale */
  color: black !important;    /* dichiarazione importante */
}

Usalo il meno possibile. !important è una scorciatoia che rompe la logica della cascata: per sovrascriverlo servirà un altro !important, e poi un altro ancora. Se ti serve, di solito il problema vero è un selettore troppo specifico da qualche altra parte.

Mettendo insieme tutto, per ogni proprietà di ogni elemento il browser decide così, dal criterio più forte al più debole:

  1. origine e importanza, dalla più debole alla più forte: browser → utente → autore → autore !important → utente !important → browser !important;
  2. specificità (stile in linea, id, classi, elementi);
  3. posizione: a parità di tutto, vince l'ultima;
  4. se nessuna regola imposta la proprietà: ereditarietà dal genitore per le proprietà ereditabili, altrimenti il valore iniziale.

Vedere la cascata: gli strumenti per sviluppatori

Il miglior alleato per capire il CSS è lo strumento di ispezione del browser: clic destro su un elemento e Ispeziona (in Chrome, Edge, Firefox e, dopo averlo attivato nelle impostazioni, in Safari). Nel pannello vedi:

  • tutte le regole che si applicano all'elemento, dalla più specifica alla meno specifica, con quelle perdenti barrate: la cascata sotto i tuoi occhi;
  • gli stili ereditati dagli antenati e quelli del foglio del browser;
  • il box model dell'elemento con contenuto, padding, bordo e margine misurati;
  • la scheda Calcolati (Computed), con il valore finale di ogni proprietà.

Si possono anche modificare i valori dal vivo e vedere subito il risultato: è il modo più veloce per sperimentare, prima di riportare nel file le modifiche che funzionano.

I colori

Nomi, esadecimali, RGB e HSL

Il CSS offre diversi modi per scrivere un colore:

MetodoCome funzionaEsempi
Nomeuno dei nomi predefiniti: 17 nei primi standard, 148 oggired, hotpink, rebeccapurple
Esadecimaletre coppie di cifre esadecimali (da 00 a FF, cioè da 0 a 255) per rosso, verde e blu, precedute da #. Se le coppie hanno cifre uguali si può abbreviare: #ff0000 = #f00#FF0000, #FF69B4, #c2410c
RGBtre numeri da 0 a 255 per rosso, verde e blurgb(255 0 0), rgb(255, 105, 180)
Trasparenzaun quarto valore, l'alfa, da 0 (trasparente) a 1 (opaco), lascia vedere ciò che sta dietrorgb(255 0 0 / 0.5), rgba(255, 0, 0, 0.5), #FF000080
HSLtonalità (da 0 a 360 gradi sul cerchio dei colori), saturazione e luminosità in percentuale. Anche qui si può aggiungere l'alfahsl(0 100% 50%) (rosso), hsl(330 100% 71%) (rosa acceso)
currentColoril valore attuale di color dell'elemento: utile per bordi e icone che seguono il colore del testoborder: 1px solid currentColor

Due notazioni convivono: quella classica con le virgole (rgba(255, 0, 0, 0.5)) e quella moderna con gli spazi e l'alfa dopo una barra (rgb(255 0 0 / 0.5)). I browser attuali accettano entrambe, e oggi rgb() e rgba() sono sinonimi.

HSL è il modo più intuitivo per ragionare sui colori: per una versione più chiara dello stesso colore si alza solo la luminosità, per una più spenta si abbassa la saturazione. Attenzione: il colore «pieno» sta al 50% di luminosità; al 100% qualsiasi tonalità diventa bianco.

Un testo deve contrastare abbastanza con il suo sfondo. Le linee guida WCAG chiedono un rapporto di contrasto di almeno 4,5:1 per il testo normale e 3:1 per quello grande. Il grigio chiaro su bianco è elegante, ma per molti lettori è illeggibile, soprattutto su un telefono al sole. Gli strumenti per sviluppatori dei browser mostrano il rapporto di contrasto quando ispezioni un testo.

Le unità di misura

Unità assolute e relative

Le unità assolute corrispondono a una dimensione fissa del mondo reale. Salvo quando si prepara un foglio di stile per la stampa, è meglio evitarle, con l'eccezione del pixel.

UnitàSignificato
inpollici (2,54 cm)
cm, mmcentimetri, millimetri
ptpunti tipografici, 1/72 di pollice
pcpica, 1/6 di pollice (12 punti)
pxpixel CSS, 1/96 di pollice

Il pixel CSS non è un pixel fisico dello schermo. Sugli schermi ad alta densità (Retina, la maggior parte dei telefoni) un pixel CSS corrisponde a 2 o 3 pixel fisici, e un centimetro CSS raramente misura un centimetro vero sullo schermo. Il pixel resta comunque l'unità di riferimento per bordi, ombre e piccoli dettagli.

Le unità relative dipendono dal valore di qualcos'altro: la dimensione del carattere, del genitore, della finestra. Sono la base di un design che si adatta.

UnitàRelativa a
emla dimensione del carattere dell'elemento stesso; usata in font-size, quella del genitore
rem(root em) la dimensione del carattere dell'elemento radice html: è la stessa in tutto il documento
%un altro valore, che dipende dalla proprietà: la larghezza del genitore per width, il carattere del genitore per font-size
vw, vhl'1% della larghezza e dell'altezza del viewport, la finestra del browser
chla larghezza del carattere «0» del font: max-width: 65ch dà righe di circa 65 caratteri, ideali da leggere
exl'altezza della «x» minuscola del font (usata raramente)

em e rem: un confronto

Le dimensioni in em e in percentuale si riferiscono al carattere del genitore, e quindi si moltiplicano a ogni livello di annidamento. Con un article al 75% dentro cui c'è un div al 75%, un paragrafo a 1em nel div misura 16 × 0,75 × 0,75 = 9 pixel. Il rem risolve il problema: si riferisce sempre alla radice, e resta costante.

calc(), min(), max() e clamp()

Le funzioni matematiche permettono di combinare unità diverse, cosa impossibile a mano:

  • calc(100% - 2rem): tutta la larghezza meno un margine fisso;
  • min(90%, 700px): il più piccolo dei due valori, cioè il 90% ma mai oltre 700 pixel;
  • max(1rem, 2vw): il più grande dei due;
  • clamp(1.5rem, 4vw, 3rem): un valore che segue 4vw ma non scende sotto il minimo e non supera il massimo. Perfetto per titoli che crescono con la finestra.

Il testo e i caratteri

Scegliere il carattere: font-family

Un programma di videoscrittura può usare qualsiasi carattere installato sul computer, e anche un browser. Il problema è che un carattere presente sul tuo computer non è per forza presente su quello di chi visita il sito. Per questo si indica uno stack di caratteri: un elenco di alternative, in ordine di preferenza.

p { font-family: Cambria, Georgia, "Times New Roman", serif; }
/*               ①        ②        ③                  ④     */

Il browser usa ① se c'è; altrimenti ②; altrimenti ③; e se non ce n'è nessuno, ④, la famiglia generica. I nomi con spazi vanno tra virgolette. L'ultima voce dovrebbe essere sempre una delle famiglie generiche, che il browser garantisce di avere:

cursive e fantasy cambiano molto da un sistema all'altro, e per questo si usano pochissimo.

La regola @font-face permette di usare un carattere anche se non è installato sul computer dell'utente: il browser lo scarica dal server insieme alla pagina. Servizi come Google Fonts offrono centinaia di caratteri liberi, pronti da collegare. Il formato da usare oggi è WOFF2, compresso e supportato ovunque.

@font-face {
  font-family: "MioCarattere";                   /* il nome con cui lo userai */
  src: url("fonts/mio-carattere.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;          /* intanto mostra il testo con un carattere di riserva */
}

body { font-family: "MioCarattere", system-ui, sans-serif; }

Con Google Fonts basta un link nella testa, e poi il nome del carattere in font-family. Questi appunti usano Inter per il testo e JetBrains Mono per il codice, caricati proprio così. Ogni carattere scaricato pesa: scegline pochi, e solo gli spessori che usi davvero.

Le proprietà dei caratteri

ProprietàValori
font-familyuno stack di caratteri, che termina con una famiglia generica
font-sizeuna lunghezza (1.25rem, 18px), una percentuale o una parola chiave (small, large)
font-weightnormal (400), bold (700), bolder, lighter, oppure un numero da 100 a 900: più alto, più spesso
font-stylenormal, italic, oblique
font-variantsmall-caps (maiuscoletto) o normal
line-heightl'altezza della riga; meglio un numero senza unità (1.5), che si moltiplica per la dimensione del carattere di ogni elemento
fontla forma abbreviata: stile, variante, spessore, dimensione/interlinea e famiglia, in quest'ordine; dimensione e famiglia sono obbligatorie
/* font: stile variante spessore dimensione/interlinea famiglia */
p { font: italic small-caps 700 1.1rem/1.6 Georgia, serif; }

In un programma di videoscrittura esprimere la dimensione in punti non crea problemi, ma le unità assolute si traducono male sugli schermi. Perfino i pixel sono problematici: gli schermi dei telefoni hanno densità sempre più alte, e un pixel CSS non corrisponde più a un pixel fisico. Soprattutto, uno dei principi del Web è che l'utente deve poter ingrandire il testo: le dimensioni relative rispettano le sue impostazioni.

Il testo predefinito del browser è di solito 16 pixel. Con em e percentuali, relative al carattere del genitore, una scala tipica è:

Sembra facile, ma basta annidare: se un article ha font-size: 75%, i suoi h1 diventano 2 × 16 × 0,75 = 24 pixel; se dentro c'è un div al 75%, 18 pixel, e i paragrafi 9. Per questo oggi si usa quasi sempre rem per le dimensioni del testo: p { font-size: 1rem; } resta 16 pixel ovunque, e segue comunque le preferenze dell'utente.

Le proprietà del paragrafo

Altre proprietà agiscono sul testo indipendentemente dal carattere scelto:

ProprietàEffettoEsempio
text-alignallineamento: left, right, center, justify, start, endtext-align: center
text-decorationsottolineato, sopralineato, barrato, o nullatext-decoration: none
text-transformmaiuscole, minuscole, iniziali maiuscoletext-transform: uppercase
text-indentrientro della prima rigatext-indent: 2em
letter-spacing, word-spacingspazio tra le lettere e tra le paroleletter-spacing: .1em
line-heightinterlinealine-height: 1.6
text-shadowombra: spostamento orizzontale, verticale, sfocatura, coloretext-shadow: 2px 2px 4px gray
white-spacecome trattare spazi e a capo: normal, nowrap (niente a capo), pre, pre-wrapwhite-space: nowrap
directiondirezione del testo, ltr o rtl (meglio l'attributo HTML dir)direction: rtl

Il box model

Ogni elemento è una scatola

Per il CSS, ogni elemento HTML sta dentro una scatola rettangolare, anche quando non si vede: un paragrafo, un titolo, un link, un'immagine. La scatola è fatta di quattro strati concentrici:

contenuto width × height padding margin border spazio interno,con lo sfondodell'elemento spazio esterno,sempre trasparente
Dall'interno verso l'esterno: contenuto, padding, bordo, margine. Lo sfondo riempie contenuto e padding e arriva fino al bordo.
  • il contenuto: testo, immagini, altri elementi;
  • il padding: lo spazio interno tra il contenuto e il bordo, colorato dallo sfondo dell'elemento;
  • il bordo (border): una linea attorno al padding, che può anche non esserci;
  • il margine (margin): lo spazio esterno che separa la scatola dalle altre, sempre trasparente.

Sposta i cursori: vedi come cambia lo spazio occupato e come lo calcola il browser.

Larghezza e altezza

Le proprietà width e height stabiliscono le dimensioni dell'area del contenuto. Si possono impostare solo sugli elementi di blocco e sugli elementi in linea «rimpiazzati», come le immagini: su un span o un a normali non hanno effetto.

Se non le imposti, valgono auto: un blocco occupa tutta la larghezza disponibile e diventa alto quanto il suo contenuto; il testo dipende da dimensione e tipo del carattere; un'immagine prende le sue dimensioni reali in pixel. Per un'immagine basta impostare una delle due, e l'altra a auto mantiene le proporzioni:

img {
  width: 300px;
  height: auto;
}

Dare un'altezza fissa a un blocco di testo è quasi sempre un errore: il testo cambia (traduzioni, caratteri più grandi scelti dall'utente) e la scatola no. Se serve un'altezza minima, usa min-height.

box-sizing: come si misura una scatola

Per impostazione predefinita, width e height indicano solo il contenuto: la dimensione totale di un elemento è la somma di contenuto, padding, bordi e margini. Con queste regole:

div {
  box-sizing: content-box;   /* il valore predefinito */
  width: 200px;
  height: 100px;
  padding: 5px;
  margin: 10px;
  border: solid 2px black;
}
  • larghezza reale occupata = 10 + 2 + 5 + 200 + 5 + 2 + 10 = 234 px;
  • altezza reale occupata = 10 + 2 + 5 + 100 + 5 + 2 + 10 = 134 px.

Con box-sizing: border-box, invece, width e height comprendono anche padding e bordo: la scatola visibile è davvero larga 200 pixel, e il contenuto si restringe per fare spazio. Lo spazio occupato con i margini diventa 10 + 200 + 10 = 220 px per 10 + 100 + 10 = 120 px. È molto più intuitivo, e quasi tutti i siti moderni cominciano il loro foglio di stile così:

*, *::before, *::after {
  box-sizing: border-box;
}

Quando il contenuto non ci sta: overflow

La proprietà overflow decide cosa succede al contenuto che esce dalla scatola:

Dimensioni relative

Gli esempi finora usano i pixel, ma spesso è meglio usare percentuali o em: con le percentuali la dimensione è relativa a quella del genitore, con gli em a quella del testo contenuto. Così il design si adatta alla finestra o al dispositivo.

Una percentuale di height funziona solo se il genitore ha un'altezza definita: per questo html e body hanno height: 100%. Cambia la larghezza dell'anteprima (o del browser) e guarda cosa si adatta e cosa no.

Due alleati per il design fluido: max-width impedisce a un elemento di diventare troppo largo (max-width: 70ch per un testo comodo da leggere, max-width: 100% perché un'immagine non esca mai dal suo contenitore), min-width e min-height impediscono che diventi troppo piccolo. Con aspect-ratio: 16 / 9 una scatola mantiene le proporzioni al variare della larghezza.

Sfondi, bordi e ombre

Le proprietà degli sfondi

Lo sfondo, colore o immagine, riempie l'elemento fino al bordo compreso. Nel web design di oggi è normalissimo usare il CSS per le immagini puramente decorative (sfumature, motivi, decorazioni) invece di img.

ProprietàEffetto
background-coloril colore di sfondo
background-imagel'immagine di sfondo, con url(…): il percorso è relativo al file CSS, non alla pagina HTML. Se ne possono sovrapporre più d'una, separate da virgole
background-repeatse e come ripetere l'immagine: repeat (predefinito, come le piastrelle), repeat-x, repeat-y, no-repeat
background-positiondove collocarla: parole chiave (top, center, right…) o una coppia di misure, orizzontale e verticale, dall'angolo in alto a sinistra
background-sizela dimensione: una misura, cover (copre tutto, ritagliando) o contain (sta tutta dentro, lasciando spazi)
background-attachmentscroll (scorre con la pagina, predefinito) o fixed (resta ferma)
backgroundla forma abbreviata di tutte le precedenti. Attenzione: le proprietà che ometti tornano al valore predefinito

Sfumature e sfondi multipli

Le sfumature sono immagini generate dal browser, senza file da scaricare: linear-gradient() lungo una direzione, radial-gradient() a partire da un centro, conic-gradient() attorno a un punto. Si usano ovunque serva un'immagine di sfondo, e si possono sovrapporre.

L'ultimo riquadro sovrappone una sfumatura nera semitrasparente a una foto: è il trucco classico per rendere leggibile un testo bianco sopra un'immagine.

Le proprietà dei bordi

ProprietàValori
border-styleil tipo di linea: solid, dotted, dashed, double, groove, ridge, inset, outset, none. Senza stile il bordo non si vede
border-widthlo spessore, in una unità di lunghezza (non in percentuale) o con thin, medium, thick
border-coloril colore (predefinito: quello del testo)
borderla forma abbreviata dei tre: border: 2px solid #c2410c, in qualsiasi ordine
border-top, border-right…un solo lato
border-radiusil raggio degli angoli arrotondati
border-imageun'immagine usata come bordo

Le forme abbreviate: in senso orario

Per border, margin e padding esistono una forma estesa, lato per lato, e una forma abbreviata. Quando la forma abbreviata ha quattro valori, si applicano in senso orario partendo dall'alto: sopra, destra, sotto, sinistra. In inglese si ricorda con TRBL (top, right, bottom, left, che si legge «trouble», guai).

1 · top 2 · right 3 · bottom 4 · left border-color: red green orange blue;
I quattro valori si leggono come le lancette di un orologio, a partire da mezzogiorno.
ValoriEsempioSignificato
1margin: 10pxtutti e quattro i lati
2margin: 10px 20pxsopra e sotto 10, destra e sinistra 20
3margin: 10px 20px 30pxsopra 10, destra e sinistra 20, sotto 30
4margin: 10px 20px 30px 40pxsopra, destra, sotto, sinistra

Ombre e contorni

box-shadow aggiunge un'ombra alla scatola: spostamento orizzontale, verticale, sfocatura, espansione e colore; con inset l'ombra è interna. outline disegna un contorno fuori dal bordo che non occupa spazio: i browser lo usano per mostrare il fuoco della tastiera, e non va mai tolto senza sostituirlo con qualcosa di altrettanto visibile.

Margini e padding

Spazio esterno e spazio interno

margin e padding accettano le stesse forme abbreviate. La differenza si vede con uno sfondo: il padding è dentro la scatola e prende il suo sfondo, il margine è fuori ed è trasparente.

Hai notato? Nella seconda e nella terza versione lo spazio tra i due paragrafi è di 30 pixel, non di 60: lo stesso che c'è sopra il primo. È il collasso dei margini.

Il collasso dei margini

Quando i margini verticali di due elementi si toccano, non si sommano: resta solo il più grande, e il più piccolo si riduce a zero. I margini orizzontali, invece, non collassano mai. Il fenomeno riguarda sia fratelli uno sopra l'altro, sia un genitore e il suo primo o ultimo figlio, quando tra i due non ci sono bordo o padding.

Tra i due div ci sono 90 pixel, non 180; tra due paragrafi 50, non 100. Il bordo tratteggiato dei div impedisce ai margini dei paragrafi di collassare con quelli del genitore: togli il bordo e guarda cosa succede.

Ci sono molti casi particolari in cui i margini non collassano: con un bordo o un padding in mezzo, dentro un contenitore Flexbox o Grid, per gli elementi flottanti o posizionati in modo assoluto. Molti sviluppatori evitano il problema dando margine agli elementi da un solo lato, per esempio solo in basso.

Un blocco con una larghezza definita si centra orizzontalmente con margin-left: auto e margin-right: auto: il browser divide lo spazio avanzato in parti uguali. È il modo classico di centrare la colonna principale di un sito.

.contenitore {
  max-width: 960px;
  margin: 0 auto;     /* sopra e sotto 0, destra e sinistra divisi a metà */
  padding: 0 16px;    /* un po' d'aria sui telefoni */
}

Display e posizionamento

display: block, inline e inline-block

La proprietà display decide come una scatola partecipa all'impaginazione. Ogni elemento ha un valore predefinito: p, div, h1, section sono di blocco; a, span, strong, em sono in linea. Con il CSS si può cambiare.

ValoreVa a capo?Larghezzawidth, height e margini verticali
blocksì, prima e dopotutta quella disponibilefunzionano
inlineno, sta nella rigaquella del contenutoignorati
inline-blockno, sta nella rigaquella del contenuto, o quella impostatafunzionano
nonel'elemento non viene disegnato e non occupa spazio, come se non ci fosse
flex, gridl'elemento è un blocco, e i suoi figli vengono disposti con Flexbox o Grid

Con inline larghezza e altezza sono ignorate e i margini verticali non spostano le righe. Con block ogni scatola va su una riga sua. Con inline-block restano nella riga ma rispettano le dimensioni: è così che si fanno i pulsanti con i link.

Per nascondere un elemento ci sono tre modi, con effetti diversi:

DichiarazioneSi vede?Occupa spazio?Lo legge un lettore di schermo?
display: nonenonono
visibility: hiddennosì: resta un bucono
opacity: 0nosìsì, ed è anche cliccabile

La proprietà position

Normalmente gli elementi seguono il flusso del documento: i blocchi uno sotto l'altro, gli elementi in linea uno dopo l'altro. La proprietà position, insieme a top, right, bottom e left, permette di cambiare le regole.

ValoreComportamentoUso tipico
staticil predefinito: segue il flusso, e top/left non hanno effettotutto
relativeresta nel flusso, ma viene spostato rispetto alla sua posizione normale; lo spazio originale resta occupatopiccoli aggiustamenti; soprattutto fare da riferimento per i figli absolute
absoluteesce dal flusso (gli altri si comportano come se non ci fosse) e si posiziona rispetto al primo antenato posizionato (non static)etichette sopra un'immagine, menu a comparsa, icone
fixedesce dal flusso e si posiziona rispetto alla finestra: resta fermo quando si scorrepulsante «torna su», banner dei cookie
stickysi comporta come relative finché, scorrendo, non raggiunge la soglia indicata; poi resta «incollato» come fixed, dentro il suo contenitoreintestazioni di tabelle, barre di navigazione

Togli position: relative dalla carta: il badge si posiziona rispetto alla pagina intera e finisce nell'angolo in alto a destra dell'anteprima.

Quando elementi posizionati si sovrappongono, z-index decide l'ordine lungo l'asse «verso chi guarda»: un numero più alto sta sopra. Funziona solo sugli elementi posizionati (non static) e sui figli di Flexbox e Grid. Attenzione: un elemento con z-index crea un proprio «contesto di sovrapposizione», e i suoi figli non possono salire sopra elementi che stanno più in alto del genitore, qualunque sia il loro numero.

float e clear

float: left o right sposta un elemento su un lato del contenitore e lascia che il testo gli scorra attorno, come le immagini in un giornale. clear: both fa ripartire un elemento sotto tutti i flottanti precedenti. Per più di dieci anni i float sono stati usati per costruire intere impaginazioni a colonne; oggi per quello ci sono Flexbox e Grid, e float torna al suo uso originale.

Flexbox

Un contenitore flessibile

Flexbox dispone gli elementi in una dimensione: in riga o in colonna. Basta dare display: flex a un contenitore: i suoi figli diretti diventano elementi flessibili e si allineano lungo l'asse principale (orizzontale, per impostazione predefinita). L'asse perpendicolare è l'asse trasversale.

1 2 3 asse principale → justify-content asse trasversale → align-items contenitore: display: flex spazio libero
Con flex-direction: column gli assi si scambiano: il principale diventa verticale.

Le proprietà del contenitore

ProprietàCosa decide
flex-directionla direzione dell'asse principale: row, row-reverse, column, column-reverse
flex-wrapse gli elementi possono andare a capo quando non ci stanno: nowrap (predefinito) o wrap
justify-contentcome distribuire gli elementi e lo spazio libero lungo l'asse principale
align-itemscome allinearli lungo l'asse trasversale; stretch (predefinito) li allunga tutti alla stessa altezza
gaplo spazio tra un elemento e l'altro

Le proprietà degli elementi

Anche i figli hanno le loro proprietà, che decidono come dividersi lo spazio:

  • flex-grow: quanto l'elemento cresce per prendersi lo spazio libero (0 = non cresce);
  • flex-shrink: quanto si restringe se lo spazio manca (0 = non si restringe);
  • flex-basis: la dimensione di partenza, prima di crescere o restringersi;
  • flex: la forma abbreviata dei tre. flex: 1 significa «prendi una parte uguale dello spazio»;
  • order: cambia l'ordine di visualizzazione senza toccare l'HTML (ma chi naviga da tastiera segue l'ordine del codice);
  • align-self: l'allineamento trasversale di un solo elemento.

Il trucco di margin-left: auto su un elemento flex assorbe tutto lo spazio libero da quel lato. Le carte vanno a capo quando non c'è posto per tre da 140 pixel: prova la vista Telefono.

Grid

Una griglia in due dimensioni

CSS Grid dispone gli elementi in due dimensioni, righe e colonne insieme. Si definiscono le colonne del contenitore con grid-template-columns, e gli elementi si sistemano da soli, cella dopo cella. Le unità più utili:

  • fr (fraction): una parte dello spazio libero. 1fr 2fr 1fr divide lo spazio in quattro parti, e dà due parti alla colonna centrale;
  • repeat(4, 1fr): quattro colonne uguali, senza ripetere;
  • minmax(200px, 1fr): almeno 200 pixel, al massimo una parte;
  • repeat(auto-fill, minmax(200px, 1fr)): quante colonne ci stanno, ognuna di almeno 200 pixel. Una griglia responsive senza nessuna media query.

Impaginare con le aree

Con grid-template-areas si «disegna» l'impaginazione con dei nomi, e poi si assegna ogni elemento alla sua area con grid-area. Cambiare l'impaginazione su uno schermo stretto diventa questione di ridisegnare il disegno.

Un elemento può anche occupare più celle: grid-column: span 2 si allarga su due colonne, grid-column: 1 / -1 va dalla prima all'ultima linea, cioè attraversa tutta la griglia. Lo stesso vale per le righe con grid-row.

Quando usare l'uno e quando l'altro?

Flexbox

Una dimensione, e il contenuto decide: gli elementi si prendono lo spazio che serve loro. Ideale per barre di navigazione, gruppi di pulsanti, allineare e centrare, righe di elementi di larghezza diversa.

Grid

Due dimensioni, e la griglia decide: prima disegni righe e colonne, poi ci metti dentro gli elementi. Ideale per l'impaginazione della pagina, gallerie, schede allineate su righe e colonne.

Non sono in competizione: una pagina tipica usa Grid per la struttura generale e Flexbox dentro i singoli componenti.

Il design responsive

Il meta viewport

Un sito responsive si adatta a qualsiasi larghezza di schermo, dal telefono al monitor, invece di avere una versione «mobile» separata. Il primo passo è nell'HTML: senza il meta viewport, i browser dei telefoni disegnano la pagina come se lo schermo fosse largo circa 980 pixel e poi la rimpiccioliscono, rendendo il testo minuscolo.

<meta name="viewport" content="width=device-width, initial-scale=1">

Con questa riga, la larghezza della pagina è quella reale del dispositivo, e le media query funzionano come previsto.

senza viewport MioSito Titolo leggibile con viewport
A sinistra la pagina da computer rimpicciolita; a destra la pagina che usa davvero la larghezza del telefono.

Le media query

Una media query applica un gruppo di regole solo se una condizione sul dispositivo è vera: la larghezza della finestra, l'orientamento, il tipo di schermo, le preferenze dell'utente.

/* regole valide sempre */
.colonne { display: grid; gap: 16px; }

/* dai 700px di larghezza in su: due colonne */
@media (min-width: 700px) {
  .colonne { grid-template-columns: 1fr 1fr; }
}

/* dai 1100px in su: tre colonne */
@media (min-width: 1100px) {
  .colonne { grid-template-columns: repeat(3, 1fr); }
}

Mobile first

La strategia più diffusa si chiama mobile first: si scrivono prima gli stili per lo schermo più piccolo, i più semplici (tutto in una colonna), e poi si aggiungono le regole per gli schermi più larghi con min-width. Le soglie (breakpoint) non vanno scelte in base ai modelli di telefono, che cambiano ogni anno, ma guardando dove il layout si rompe: allarga la finestra finché qualcosa non sta più bene, e lì metti una media query.

Le media query possono chiedere molto altro:

CondizioneVera quando…
(min-width: 700px), (max-width: 699px)la finestra è almeno / al massimo così larga
(orientation: landscape)lo schermo è più largo che alto
(hover: hover), (pointer: coarse)c'è un mouse / il puntatore è un dito, poco preciso
(prefers-color-scheme: dark)l'utente ha scelto il tema scuro nel sistema operativo
(prefers-reduced-motion: reduce)l'utente ha chiesto meno animazioni (per chi soffre di chinetosi)
printla pagina viene stampata

Nell'anteprima vedi la versione per lo schermo. Le regole dentro @media print scattano solo in stampa (o nell'anteprima di stampa del browser).

Variabili, transizioni e animazioni

Le variabili CSS

Le proprietà personalizzate, dette comunemente variabili CSS, permettono di dare un nome a un valore e riusarlo ovunque. Si dichiarano con due trattini davanti al nome e si usano con var(). Di solito si dichiarano su :root (cioè l'elemento html) perché valgano in tutta la pagina.

:root {
  --accento: #c2410c;
  --spazio: 16px;
}
.pulsante { background: var(--accento); padding: var(--spazio); }
.titolo   { color: var(--accento); margin-bottom: var(--spazio); }
.nota     { color: var(--secondario, gray); }   /* gray se la variabile non esiste */

A differenza delle variabili dei preprocessori, le variabili CSS sono vive: si ereditano come le proprietà di testo, si possono ridefinire dentro un elemento o in una media query, e il cambiamento si propaga subito a tutto ciò che le usa. È così che si costruisce un tema scuro.

In un sito vero, invece della classe .scuro si userebbe @media (prefers-color-scheme: dark) { :root { … } }, così il tema segue la scelta fatta dall'utente nel sistema operativo.

Le transizioni

Normalmente un cambiamento di stile (per esempio al passaggio del mouse) è istantaneo. Con transition il browser lo rende graduale: indichi quali proprietà animare, per quanto tempo e con quale andamento (ease, linear, ease-in-out…).

Le trasformazioni

transform sposta, ruota, ingrandisce o inclina un elemento senza toccare l'impaginazione: gli altri elementi non se ne accorgono. È anche la proprietà più efficiente da animare, insieme a opacity.

Le animazioni con @keyframes

Una transizione va da uno stato all'altro quando qualcosa cambia. Un'animazione invece segue una sequenza di fotogrammi chiave definita con @keyframes, e può partire da sola e ripetersi.

La forma abbreviata animation indica nome, durata, andamento e ripetizioni. L'ultima regola rallenta tutto per chi ha chiesto meno movimento nelle impostazioni del sistema: rispettare questa preferenza è una questione di accessibilità.

Le rifiniture

Liste e menu

Le liste hanno le loro proprietà: list-style-type (disc, circle, square, decimal, lower-roman, none…), list-style-position e list-style-image. Con list-style: none e un contenitore Flexbox, una normale lista di link diventa un menu orizzontale: è la base di quasi tutte le barre di navigazione.

Opacità, filtri e cursore

Verifica