Un altro scenario frequente e reale in cui il flexbox si rivela la soluzione perfetta è quello dei layout in cui si abbia la necessità di avere colonne affiancate con altezza uguale a prescindere dai contenuti ospitati nelle colonne. È un problema annoso per il quale sono state trovate nel corso del tempo soluzioni ingegnose e robuste, sia basate sui soli CSS sia implementate via Javascript come nel componente Equalizer del framework Foundation. Con flexbox tutto diventa più immediato, una soluzione non basata su hack ma semplicemente fondata sulle funzionalità native di questa parte dei CSS.
Di default, infatti, quando si creano colonne affiancate disposte su una riga all'interno di un contenitore flessibile, il valore della proprietà align-items
è impostato sul valore stretch
, quello che fa sì che le colonne si estendano, appunto, per occupare tutto lo spazio disponibile verticalmente. L'altezza di ciascuna colonna viene automaticamente calcolata per essere uguale a quella della colonna più lunga perché ha più contenuto.
Nell'analisi delle demo di questa sezione possiamo dunque partire da un esempio in cui per creare il layout sono stati usati i float. Senza intervenire con altre tecniche per estendere le colonne, ci ritroviamo in questa situazione:
Vediamo come abbiamo usato il flexbox per ottenere il risultato della demo 7, illustrato dalla seguente schermata:
Figura 8 - Colonne ad altezza uguale con flexbox
I tre box (colonne) della semplice griglia sono contenuti in un elemento con classe .cards
:
.cards {
display: flex;
display: -webkit-flex;
flex-flow: row nowrap;
-webkit-flex-flow: row nowrap;
align-items: stretch;
-webkit-align-items: stretch;
justify-content: space-between;
-webkit-justify-content: space-between;
margin-top: 10px;
}
Lo dichiariamo display: flex
. Impostiamo la proprietà flex-flow
con i valori row
(gli elementi si dispongono su una riga) e nowrap
(impediamo agli elementi di disporsi su più righe).
Abbiamo anche specificato il valore stretch
per align-items
, giusto a fini didattici, perché come si accennava è il valore di default. Infine, facciamo sì che gli item si dispongano orizzontalmente creando una spaziatura proporzionale tra di loro (justify-content: space-between
).
Fatto. I box contenuti in .cards-container
avranno altezza uguale con queste semplici istruzioni. La chiave, ribadiamo, è la proprietà align-items: stretch
.
Il lavoro non è però completo. Vogliamo anche che il pulsante posto in basso sia ancorato sempre sul fondo del box. In questo caso dobbiamo lavorare sui singoli componenti della griglia e non semplicemente sul loro contenitore.
Passiamo dunque ai box che compongono la griglia. Sono definiti da un div con classe .card-container
:
.card-container {
display: flex;
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
width: 33.3%;
padding: 10px;
}
Dato che vogliamo che gli elementi che contiene siano flessibili, dichiariamo anche per esso display: flex
. E poiché tali elementi si disporranno verticalmente, impostiamo flex-direction
su column
invece che row
.
La larghezza (width
) e il padding servono a dare una dimensione orizzontale e a spaziare i tre box.
Ciascun box è poi suddiviso in due blocchi. Il primo ospita un'immagine flessibile che si adatta automaticamente alla larghezza del suo contenitore. La tecnica è quella classica con max-width: 100%
che abbiamo visto all'opera nell'articolo Immagini flessibili e nella guida al responsive design. Ecco la regola per l'immagine, per il div .card-image
non è necessario specificare alcuna regola:
.card-image img {
display: block;
height: auto;
margin: 0 auto;
max-width: 100%;
width: 100%;
}
Il componente del box più problematico è quello con il contenuto testuale, identificato dalla classe card-content
. Dato che questo contenuto è variabile e che da esso dipende l'altezza reale del div che contiene il testo e il pulsante, quest'ultimo verrà ovviamente a collocarsi ad altezze diverse, come illustrato da questo screenshot. Abbiamo sì altezze uguali per i box, ma il pulsante non è ancorato sul fondo.
Figura 9 - Pulsante ad altezze diverse
Per risolvere questo problema di allineamento del pulsante la soluzione immediata sarebbe ricorrere, come nel caso già visto del form di ricerca, al margine automatico, applicato a margin-top
visto che vogliamo un allineamento sul lato inferiore. La cosa, da sola, non è però sufficiente, perché quando operiamo in verticale, avendo come riferimento l'altezza e non la larghezza, è necessario fornire un riferimento, un contesto di posizionamento, uno spazio in verticale rispetto al quale il pulsante possa collocarsi sul fondo del suo contenitore. Insieme al margine automatico, dunque, dovremo assegnare al div .card-content
anche un flex: 1
. Con questa dichiarazione diciamo: cresci ed espanditi fino ad occupare tutto lo spazio disponibile.
Ecco la regola CSS che usiamo per impostare il div:
.card-content {
display: flex;
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
padding: 10px 0;
}
.mgtop-auto {margin-top: auto;}
Semplificando molto, abbiamo fatto questo:
- il div con l'immagine non è flessibile, ha
flex-grow
impostato su 0, per cui occuperà verticalmente lo spazio dettato dal suo contenuto; - il div con il contenuto testuale, invece, ha, tramite la proprietà
flex
, unflex-grow
pari a 1, che lo fa diventare flessibile per fargli occupare tutto lo spazio disponibile; ciò crea anche un contesto per l'altezza che ci consente di applicare l'allineamento in basso conmargin-top: auto;
.
Il problema delle colonne uguali in altezza è risolto in modo elegante e semplice, naturale, perché flexbox è fatto esattamente per questo.