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
| Versione | Data | Novità |
|---|---|---|
| CSS livello 1 | 17 dicembre 1996 | caratteri, colori, testo, margini e bordi |
| CSS livello 2 | 12 maggio 1998 | posizionamento, tipi di media, selettori più potenti |
| CSS 2.1 | 7 giugno 2011 | revisione di CSS2 che corregge gli errori e registra ciò che i browser implementano davvero |
| «CSS3» e oltre | dal 2011 | il 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.
- 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
| Categoria | Proprietà |
|---|---|
| Caratteri | font, font-family, font-size, font-style, font-weight, @font-face |
| Testo | letter-spacing, line-height, text-align, text-decoration, text-indent, text-transform, text-shadow |
| Colore e sfondo | color, background, background-color, background-image, background-position, background-repeat, box-shadow, opacity |
| Bordi | border, border-color, border-width, border-style, border-top… border-radius, border-image |
| Spaziature | padding, padding-top…, margin, margin-top… |
| Dimensioni | width, height, min-width, max-width, min-height, max-height |
| Impaginazione | display, position, top, right, bottom, left, float, clear, overflow, visibility, z-index, gap |
| Liste | list-style, list-style-type, list-style-image |
| Effetti | transform, 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:
- il foglio del browser (user agent stylesheet), con gli stili predefiniti di ogni elemento: è per questo che un
h1senza CSS è grande e in grassetto; - il foglio dell'utente: nelle opzioni di accessibilità del browser, chiunque può impostare caratteri o colori preferiti;
- 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.
| Selettore | Seleziona gli elementi… | Esempio |
|---|---|---|
[attr] | che hanno l'attributo, con qualsiasi valore | a[title] |
[attr="v"] | con valore esattamente v | a[href="https://example.org"] |
[attr~="v"] | il cui valore è un elenco di parole separate da spazi, una delle quali è esattamente v | a[class~="logo"] |
[attr^="v"] | il cui valore inizia con v | a[href^="https"] |
[attr$="v"] | il cui valore finisce con v | a[href$=".pdf"] |
[attr*="v"] | il cui valore contiene v | a[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-classe | Seleziona |
|---|---|
:link, :visited | link non ancora visitati / già visitati |
:hover | l'elemento sotto il puntatore del mouse |
:active | l'elemento mentre viene premuto |
:focus, :focus-visible | l'elemento che ha il fuoco (il secondo solo quando il fuoco va mostrato, per esempio da tastiera) |
:checked, :disabled, :required, :valid, :invalid | stati dei campi dei moduli |
:first-child, :last-child, :only-child | primo, 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 |
:empty | gli elementi senza figli né testo |
:target | l'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-elemento | Seleziona |
|---|---|
::before, ::after | un elemento virtuale creato come primo o ultimo figlio, con il contenuto della proprietà content |
::first-letter | la prima lettera di un blocco: i capilettera |
::first-line | la prima riga di un blocco, qualunque sia la larghezza |
::marker | il pallino o il numero delle voci di lista |
::placeholder | il testo segnaposto di un campo |
::selection | il 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.
| Combinatore | Nome | Esempio | Seleziona |
|---|---|---|---|
| spazio | discendente | div p | i p che stanno ovunque dentro un div, a qualsiasi profondità |
> | figlio | div > h2 | gli h2 figli diretti di un div |
+ | fratello adiacente | h3 + p | il p che viene subito dopo un h3, con lo stesso genitore |
~ | fratello generico | h3 ~ p | tutti 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:
- ereditarietà;
- specificità;
- 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:
- a: 1 se la dichiarazione è in un attributo
style, altrimenti 0; - b: il numero di id nel selettore;
- c: il numero di classi, attributi e pseudo-classi;
- 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.
| Selettore | a | b (id) | c (classi, attributi, pseudo-classi) | d (elementi) |
|---|---|---|---|---|
p | 0 | 0 | 0 | 1 |
div p | 0 | 0 | 0 | 2 |
.last | 0 | 0 | 1 | 0 |
p.last:hover | 0 | 0 | 2 | 1 |
a[href$=".pdf"]::after | 0 | 0 | 1 | 2 |
#verylast | 0 | 1 | 0 | 0 |
#main div p:first-child | 0 | 1 | 1 | 2 |
style="color: red" | 1 | 0 | 0 | 0 |
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:
- origine e importanza, dalla più debole alla più forte: browser → utente → autore → autore
!important→ utente!important→ browser!important; - specificità (stile in linea, id, classi, elementi);
- posizione: a parità di tutto, vince l'ultima;
- 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:
| Metodo | Come funziona | Esempi |
|---|---|---|
| Nome | uno dei nomi predefiniti: 17 nei primi standard, 148 oggi | red, hotpink, rebeccapurple |
| Esadecimale | tre 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 |
| RGB | tre numeri da 0 a 255 per rosso, verde e blu | rgb(255 0 0), rgb(255, 105, 180) |
| Trasparenza | un quarto valore, l'alfa, da 0 (trasparente) a 1 (opaco), lascia vedere ciò che sta dietro | rgb(255 0 0 / 0.5), rgba(255, 0, 0, 0.5), #FF000080 |
| HSL | tonalità (da 0 a 360 gradi sul cerchio dei colori), saturazione e luminosità in percentuale. Anche qui si può aggiungere l'alfa | hsl(0 100% 50%) (rosso), hsl(330 100% 71%) (rosa acceso) |
currentColor | il valore attuale di color dell'elemento: utile per bordi e icone che seguono il colore del testo | border: 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 |
|---|---|
in | pollici (2,54 cm) |
cm, mm | centimetri, millimetri |
pt | punti tipografici, 1/72 di pollice |
pc | pica, 1/6 di pollice (12 punti) |
px | pixel 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 |
|---|---|
em | la 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, vh | l'1% della larghezza e dell'altezza del viewport, la finestra del browser |
ch | la larghezza del carattere «0» del font: max-width: 65ch dà righe di circa 65 caratteri, ideali da leggere |
ex | l'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 segue4vwma 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-family | uno stack di caratteri, che termina con una famiglia generica |
font-size | una lunghezza (1.25rem, 18px), una percentuale o una parola chiave (small, large) |
font-weight | normal (400), bold (700), bolder, lighter, oppure un numero da 100 a 900: più alto, più spesso |
font-style | normal, italic, oblique |
font-variant | small-caps (maiuscoletto) o normal |
line-height | l'altezza della riga; meglio un numero senza unità (1.5), che si moltiplica per la dimensione del carattere di ogni elemento |
font | la 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à | Effetto | Esempio |
|---|---|---|
text-align | allineamento: left, right, center, justify, start, end | text-align: center |
text-decoration | sottolineato, sopralineato, barrato, o nulla | text-decoration: none |
text-transform | maiuscole, minuscole, iniziali maiuscole | text-transform: uppercase |
text-indent | rientro della prima riga | text-indent: 2em |
letter-spacing, word-spacing | spazio tra le lettere e tra le parole | letter-spacing: .1em |
line-height | interlinea | line-height: 1.6 |
text-shadow | ombra: spostamento orizzontale, verticale, sfocatura, colore | text-shadow: 2px 2px 4px gray |
white-space | come trattare spazi e a capo: normal, nowrap (niente a capo), pre, pre-wrap | white-space: nowrap |
direction | direzione 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:
- 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-color | il colore di sfondo |
background-image | l'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-repeat | se e come ripetere l'immagine: repeat (predefinito, come le piastrelle), repeat-x, repeat-y, no-repeat |
background-position | dove collocarla: parole chiave (top, center, right…) o una coppia di misure, orizzontale e verticale, dall'angolo in alto a sinistra |
background-size | la dimensione: una misura, cover (copre tutto, ritagliando) o contain (sta tutta dentro, lasciando spazi) |
background-attachment | scroll (scorre con la pagina, predefinito) o fixed (resta ferma) |
background | la 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-style | il tipo di linea: solid, dotted, dashed, double, groove, ridge, inset, outset, none. Senza stile il bordo non si vede |
border-width | lo spessore, in una unità di lunghezza (non in percentuale) o con thin, medium, thick |
border-color | il colore (predefinito: quello del testo) |
border | la forma abbreviata dei tre: border: 2px solid #c2410c, in qualsiasi ordine |
border-top, border-right… | un solo lato |
border-radius | il raggio degli angoli arrotondati |
border-image | un'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).
| Valori | Esempio | Significato |
|---|---|---|
| 1 | margin: 10px | tutti e quattro i lati |
| 2 | margin: 10px 20px | sopra e sotto 10, destra e sinistra 20 |
| 3 | margin: 10px 20px 30px | sopra 10, destra e sinistra 20, sotto 30 |
| 4 | margin: 10px 20px 30px 40px | sopra, 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.
| Valore | Va a capo? | Larghezza | width, height e margini verticali |
|---|---|---|---|
block | sì, prima e dopo | tutta quella disponibile | funzionano |
inline | no, sta nella riga | quella del contenuto | ignorati |
inline-block | no, sta nella riga | quella del contenuto, o quella impostata | funzionano |
none | l'elemento non viene disegnato e non occupa spazio, come se non ci fosse | ||
flex, grid | l'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:
| Dichiarazione | Si vede? | Occupa spazio? | Lo legge un lettore di schermo? |
|---|---|---|---|
display: none | no | no | no |
visibility: hidden | no | sì: resta un buco | no |
opacity: 0 | no | sì | 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.
| Valore | Comportamento | Uso tipico |
|---|---|---|
static | il predefinito: segue il flusso, e top/left non hanno effetto | tutto |
relative | resta nel flusso, ma viene spostato rispetto alla sua posizione normale; lo spazio originale resta occupato | piccoli aggiustamenti; soprattutto fare da riferimento per i figli absolute |
absolute | esce 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 |
fixed | esce dal flusso e si posiziona rispetto alla finestra: resta fermo quando si scorre | pulsante «torna su», banner dei cookie |
sticky | si comporta come relative finché, scorrendo, non raggiunge la soglia indicata; poi resta «incollato» come fixed, dentro il suo contenitore | intestazioni 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.
flex-direction: column gli assi si scambiano: il principale diventa verticale.Le proprietà del contenitore
| Proprietà | Cosa decide |
|---|---|
flex-direction | la direzione dell'asse principale: row, row-reverse, column, column-reverse |
flex-wrap | se gli elementi possono andare a capo quando non ci stanno: nowrap (predefinito) o wrap |
justify-content | come distribuire gli elementi e lo spazio libero lungo l'asse principale |
align-items | come allinearli lungo l'asse trasversale; stretch (predefinito) li allunga tutti alla stessa altezza |
gap | lo 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: 1significa «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 1frdivide 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.
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:
| Condizione | Vera 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) |
print | la 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.