CSS e margini verticali che collassano. Tutte le soluzioni!

Come molti comportamenti HTML e CSS di base, il concetto dei margini che collassano può portare a risultati inattesi e talvolta non intuitivi.

Questo articolo rappresenta una traduzione autorizzata dall’ autore Eric A. Meyer, di cui potete leggere l’articolo originale “Uncollapsing margins“, e che serve per capire come controllare i margini.
Prima di esplorare questi risultati e il modo di lavorarci, però, diamo uno sguardo alla forma più elementare di margine che collassa, uno degli aspetti fondamentali del box model.

Si consideri una serie di paragrafi che hanno il seguente stile CSS:

p {margin: 10px 0; background: gold;}

La separazione tra i bordi esterni di questi paragrafi sarà di dieci pixel, come mostrato in questo esempio.

Perchè succede così? Perché è quello che i progettisti di una pagina HTML tenderebbero ad aspettarsi.
Se si dichiarano margini superiore e inferiore di 1em per i paragrafi, le probabilità sono che si desidera uno em di spazio tra i paragrafi successivi.

Se questa affermazione non vi convince, considerate quanto segue:

p {margin: 1em 0;}
ul {margin: 1em 0;}

Molto probabilmente ciò che l’autore si aspetta, date queste semplici regole, è che la separazione tra un paragrafo e l’elenco sarà di un em, non due em. Se si desidera di più, allora le regole CSS possono essere reimpostate; per esempio, potremmo aumentare i margini dell’elemento ul.

Titoli che si avvicinano

Un effetto tipografico comune è quello di inserire il testo in paragrafi inseriti subito dopo un titolo. Alcune persone risolvono questa situazione con l’aggiunta di una classe a un paragrafo che segue immediatamente una intestazione, e quindi rimuovendo il margine superiore da tali paragrafi.
Grazie ai margini che collassano, però, c’è un modo più elegante per risolvere il problema. Prendete in considerazione questo esempio:

h1, h2, h3, h4, h5, h6 {margin-bottom: 0;}
p {margin: 0 0 1em;}

A fronte di tali regole, ogni paragrafo che segue immediatamente un titolo si appoggerà proprio accanto ad esso, mentre i paragrafi successivi saranno ancora separati da una “riga vuota”. Poiché il collasso dei margini tra i paragrafi fa in modo che i margini superiore e inferiore collassino, possiamo rimuovere uno dei due senza modificare per niente la situazione del layout. Quindi, la presenza del margine inferiore sui paragrafi li mantiene alla consueta distanza reciproca, pur consentendo loro di restare vicini alle intestazioni.

Avendo stabilito tutto questo, parliamo adesso delle situazioni in cui il collassamento dei margini produce risultati che i progettisti del web raramente vogliono vedere!

Eccessi…marginali!

Il cavallo di battaglia della disposizione tableless è l’uso del div. Barre laterali, testate, piè di pagina e molto altro, spesso  – sempre meno, grazie all’introduzione del web semantico – sono definiti da un div con le classi (o gli ID opportuni). A parte la sua relativa neutralità semantica, il div è particolarmente adatto a questo scopo, perché è un elemento di flusso –  o a blocco, che in HTML è un elemento che può contenere sia elementi di blocco e in linea. In altre parole, ci si può mettere dentro praticamente qualsiasi elemento.

L’altro vantaggio principale del div, è che genera tradizionalmente un box a livello di blocco, ma non ha altre caratteristiche di presentazione intrinseche. In termini CSS, il foglio di stile integrato nel browser potrebbe affermare, semplicemente:

div {display: block;}

Sorprendentemente, questo semplice pezzo di codice, nasconde un potenziale problema di layout – uno di quelli che sorgono non a causa di qualcosa che il progettista web fa, ma più a causa di ciò che omette di fare. Possiamo illustrare questo problema abbastanza facilmente, semplicemente aggiungendo due elementi e un po’ di stile. Si consideri il seguente codice:

#masthead {background: #F80; margin: 10px;}
#masthead h1 {margin: 20px 10px;}
#masthead p {margin: 5px 10px; font-style: italic;}

Notate quanto la parte superiore del tag h1 si dispone vicino all’inizio della zona di fondo del div? Ci si potrebbe aspettare di trovare venti pixel di margine tra la parte superiore del tag h1 e la parte superiore del div. Invece, il margine superiore del h1 effettivamente “si attacca” al div che lo contiene. I due margini effettivamente si sovrappongono. Lo stesso vale per il margine inferiore dell’elemento p e il margine inferiore dell’elemento div.

Guardiamo le liste

Lo stile e il markup che illustreranno questo punto sono semplici come il nostro esempio precedente:

li {background: #FB8; margin: 2px;}
p {margin: 10px;}

In questo esempio, assumiamo che la voce di elenco sia parte di una lista non ordinata: gli elementi dell’elenco possono contenere paragrafi, perché, come i div, sono elementi a livello di flusso, e quindi, un elemento di elenco può contenere ciascun marcatore che un div può contenere.

Otteniamo lo stesso risultato inaspettato e non voluto!

Soluzioni per…non collassare!

Al fine di far non “collassare” i margini, ci sono principalmente due soluzioni di base: usare il padding o i bordi. La chiave è quella di usarli in modo appropriato: quale soluzione sia la migliore, dipenderà dai requisiti di progettazione per un dato progetto.

In entrambi i casi, è necessario applicare il padding o il border-top al contenitore. Consideriamo prima il padding. Dobbiamo aggiungere il più piccolo padding possibile al contenitore, solo un pixel (o addirittura 0.04px) sulla parte superiore o inferiore, e nessuno sui lati, come in questo esempio.

Oltre alle specifiche dettate dal W3C su come risolvere il problema, ecco alcune soluzioni trovate cercando su stackoverflow:

  • Far flottare gli elementi
  • Rendere gli elementi “inline-block
  • Impostare al contenitore la proprietà “overflow” ad “auto” (o qualsiasi valore che non sia “visible”)
  • Applicare un padding di 0.04px al contenitore

Oppure:

Applicare al contenitore queste regole:

#contenitore:before, #contenitore:after{
    content: ' ';
    display: table;
}

O, infine, applicare un bordo all’elemento:

#contenitore {border-top:0.02px solid transparent;}

Come detto in precedenza, spetta al progettista web trovare la soluzione migliore per il progetto che sta sviluppando.