Flexbox senza segreti: il container

Si può dire che il flexbox layout nel CSS ha rivoluzionato il modo di strutturare gli elementi del nostro HTML. Tuttavia qualche regola è difficile da digerire anche per i più esperti tra proprietà del container, proprietà dei children e valori non proprio immediati. Ecco quindi la guida completa per conoscere tutti i segreti del flexbox.

Mag 9, 2024 | Web Development

Benvenuti in un nuovo articolo sullo sviluppo web.

Oggi parliamo del layout flexbox, un nuovo box model che ha cambiato ed evoluto il modo di impostare la struttura dei nostri box di HTML. Dopo anni di block, inline, table e positioned, flex introdusse un nuovo sistema per gestire layout complessi di applicazioni e pagine web, rendendo letteralmente “flessibili” i container del codice in HTML per adattarsi alle moderne esigenze di design. Non solo: con gli opportuni accorgimenti talvolta rende superflua l’aggiunta di media-queries per il responsive.

Il flexbox, in termini pratici, non è altro che un valore dato alla proprietà display. In questo modo quel determinato contenitore diventa "flessibile".

In questo articolo mi limiterò a descrivere le regole applicabili al container flex, per evitare di scrivere un papiro infinito e noioso. Arriverà comunque la seconda parte con le regole del child o degli item di un flex-container. Promesso.

Un po' di contesto

Quello che chiameremo d’ora in poi flexbox fu introdotto nell’ottobre 2017 con il W3C Candidate Recommendation, un documento redatto dal World Wide Web Consortium, l’organizzazione non governativa internazionale che si occupa di stabilire gli standard tecnici dei linguaggi di markup. Tralasciando i tecnicismi, possiamo dire che si tratta di un insieme di regole del CSS che gestiscono il comportamento "flessibile" dei div, section o altri container del nostro codice HTML.

L’idea di base era quella di rendere i container, appunto, flessibili (da qui il nome) per favorire l’allineamento, il dimensionamento e la spaziatura del layout anche in assenza di misure prestabilite o sconosciute.

In termini pratici, il layout flexbox ha l’esplicito obiettivo di modificare altezza, larghezza, ordine e altre caratteristiche del container per distribuire nel modo migliore lo spazio al suo interno, occupato da gap e da div con del contenuto. Questo avviene grazie alla capacità dei children di restringersi o allargarsi in base alle esigenze e alle proprietà assegnate.

Nomenclatura e concetti di base

Per agevolare la comunicazione vediamo la nomenclatura di base che ci serve per comprendere le regole/proprietà che analizzeremo e che ci permetteranno di gestire il nostro container flexbox. Il documento citato nel paragrafo precedente ad opera della W3C riporta uno schema molto chiaro delle misure e delle dimensioni da prendere in considerazione:

Figura 1 - Schema di un layout flexbox proposto dal W3C.

Lo schema riportato differisce dall'originale per l'aggiunta del padding al flex-container. Per questo motivo tutti i vari main e cross size/start/end partono dagli estremi del container escludendo il padding.

Vediamo le misure che ci indica lo schema.

  • Main axis e cross axis, concetti fondamentale per il flexbox, non sono altri che la direzione su cui si dispongono i vari flex item. Il layout flex non prende in considerazione la direzione orizzontale o verticale in quanto tali, ma piuttosto definisce un asse principale (che può essere orizzontale o verticale, a seconda di come è impostata la proprietà flex-direction che vedremo dopo) o longitudinale e una secondaria o perpendicolare. Molte proprietà variano il proprio risultato a seconda di quale tra i due sia il main axis.
aldo giovanni giacomo pignolo
Main axis e cross axis non sono assi cartesiani
Piccola nota dal livello di pignoleria veramente alto. Gli assi main e cross sono direzioni, non assi cartesiani. Per quanto riguarda flexbox a noi serve solo parlare di disposizione degli elementi orizzontale o verticale; perciò non è necessario definire un sistema di coppie di numeri che individua i punti del piano così come avveniva per clip-path. Lo abbiamo visto in questo articolo.
  • Main size e cross size sono le dimensioni del flex container, del contenitore a cui è stato assegnato il layout flex. Così come per gli axis, main size e cross size sono intercambiabili, ovvero possono essere orizzontali o verticali, a seconda del valore della proprietà flex-direction.
  • Main start e main end sono rispettivamente il punto iniziale e finale del main axis alla base della disposizione longitudinale (lungo l’asse) dei flex item. La distanza tra start ed end corrisponde alla main size del container.
  • La definizione di cross start e cross end coincide con quella di main start e main end con la differenza che in questo caso consideriamo l’asse perpendicolare o cross axis.

Non preoccupatevi: flexbox dopo questo schema sembra più complicato di quel che è in realtà. Ora mettiamo le mani in pasta con qualche esempio pratico e sarà tutto più chiaro.

Le regole del flexbox: premesse

Per parlare di flexbox e delle principali proprietà per gestirlo non c’è nulla di meglio che applicarle direttamente. Consideriamo il seguente codice HMTL e relativo CSS:

<section class="flex-container">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
</section>
.flex-container {
  /*Regole per centrare il container rispetto al body*/
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  
  /*Un po' di stile per capire meglio il risultato*/
  background: #87C7D6;
  padding: 20px;
  
  /*Regole del flexbox*/
  display: flex;
  gap: 10px;
}

.flex-item {
  /*Regole per centrare i numeri*/
  display: flex;
  justify-content: center;
  align-items: center;
  
  /*Stile dei flex-item*/
  width: 100px;
  height: 100px;
  background: #3D8CA8;
  color: white;
  font-size: 1.5rem;
  font-family: sans-serif;
}

Abbiamo ottenuto questo risultato:

Figura 2 - Il risultato ottenuto con il codice dell'esempio.

Tralasciamo completamente tutte le regole per il posizionamento e lo stile del .flex-container: le ho inserite solo per centrare la nostra section alla pagina e vedere chiaramente cosa stiamo facendo, nel caso voleste copiare il codice. Per approfondire altri modi per centrare un contenitore, recuperate questo articolo. Tralasciate anche tutte le regole del .flex-item, perché non ci interessano per ora.

Concentriamoci sul resto. In realtà non c’è nulla di particolare da capire: ho inserito un contenitore con 3 elementi e ho assegnato due proprietà, display: flex e gap. La prima serve banalmente per inizializzare il comportamento flex del container e la seconda per dare un piccolo spazio tra i 3 flex-item quadrati. Ho aggiunto anche agli item una serie di stili per centrare il contenuto (la numerazione) e descriverli visivamente. Il flex-container sarà in gold mentre i flex-item sono riconoscibili in viola.

Notiamo una cosa: inserendo tutto il codice tranne display: flex i flex-item si dispongono uno sotto l’altro. Questo avviene perché di default la proprietà display di <section> è block e di conseguenza occupa tutta la larghezza disponibile. Aggiungendo display: flex gli item si allineano uno dopo l’altro; aiutandoci con la funzione “Ispeziona” di Chrome o di qualsiasi altro browser, capiamo che ora il flex-container occupa lo spazio minimo necessario per contenere gli item più chiaramente il gap. Sottolineo che nel nostro caso non abbiamo impostato una precisa width o height al flex-container.

Tutte le regole che tratteremo da ora in poi sono applicabili al flex-container.

Flex-direction

Come dicevamo alcuni paragrafi fa, una delle proprietà fondamentali è la flex-direction. Tramite questa proprietà possiamo settare la direzione del main axis attraverso uno dei seguenti valori:

  • flex-direction: row è l’impostazione di default. La disposizione degli item avverrà in riga (orizzontale) in successione da sinistra verso destra in orizzontale;
  • flex-direction: row-reverse produce lo stesso risultato di row, solo che gli elementi saranno disposti da destra verso sinistra;
  • flex-direction: column invece dispone gli elementi in colonna dall’alto verso il basso;
  • flex-direction: column-reverse dispone gli elementi in colonna ma dal basso verso l’alto.
Figura 3 - Lo schema mostra come si dispongono i flex-item a seconda del valore di flex-direction.

Attenzione alla direction

Non parlo di flex-direction, ma proprio di direction. Si tratta di una proprietà usata di rado, tanto che la documentazione sconsiglia di usarla, che permette di impostare la direzione di disposizione degli elementi come testi o tabelle verso destra o sinistra. In termini tecnici imposta il flow orizzontale. Di default è settato con il valore ltr (left to right) implicito ma è importante considerarlo perché la flex direction: row e row-reverse sono strettamente correlate alla direction, che ribalta la successione degli elementi.

Ecco cosa succede in questi quattro casi:

  • flex direction: row e direction: ltr - gli elementi sono disposti da sinistra a destra;
  • flex direction: row e direction: rtl - gli elementi sono disposti da destra a sinistra;
  • flex direction: row-reverse e direction: ltr - gli elementi sono disposti da destra a sinistra;
  • flex direction: row-reverse e direction: rtl - gli elementi sono disposti da sinistra a destra;

Flex-wrap

Di default i flex-item si dispongono sulla stessa linea, uno dopo l’altro. Se lo spazio a disposizione non fosse sufficiente per contenere tutti gli elementi, questi, essendo flessibili, si restringeranno per adattarsi. A seconda del contenuto e del layout che stiamo creando difficilmente la nostra intenzione sarà quella di restringere i nostri flex-item, almeno non oltre un certo punto.

Una delle soluzioni consiste nell'applicare al flex-container la proprietà flex-wrap: wrap. In questo modo i flex-item andranno a capo (o nella colonna successiva a seconda del main axis), occupando lo spazio prestabilito senza restringersi né allargarsi.

aldo giovanni giacomo pignolo
Lo "spazio disponibile"
Quando parlo di "spazio disponibile", intendo quello del flex container. Però abbiamo detto che il flex-container assume la larghezza e l'altezza minima necessaria a contenere i flex-item, quindi teoricamente non dovrebbe esserci spazio da utilizzare. Il problema si risolve ricordando che nulla ci impedisce di assegnare una width e una height a nostro piacimento al flex-container, aumentando di fatto lo spazio disponibile in larghezza o altezza..

flex-wrap può assumere tre valori:

  • flex-wrap: nowrap - è il valore di default, che forza l’allineamento di tutti i flex-item nella stessa riga o nella stessa colonna, sfruttando un’altra proprietà di default che vedremo nel prossimo articolo, quando parleremo delle proprietà applicabili ai flex-item.
  • flex-wrap: wrap - come abbiamo detto consente di “andare a capo” ovvero occupare un’altra riga o colonna a seconda dello spazio disponibile.
  • flex-wrap: wrap-reverse - produce lo stesso comportamento di flex-wrap: wrap ma al contrario. Questo significa che la successione delle nuove righe sarà dal basso verso l’alto, mentre le nuove colonne saranno disposte da destra a sinistra.

Per riprendere sempre il nostro esempio, ipotizziamo di fare alcune modifiche: aggiungiamo due flex-item e settiamo la larghezza del flex-container a 320px in questo modo:

<section class="flex-container">
  [...]
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
</div>
.flex-container {
  [...]
  width: 320px;  
}

Sintetizziamo i risultati ottenuti assegnando i vari valori di flex-wrap in questo schema:

Figura 4 - Gli effetti dei valori di flex-wrap.

Allineamento: main axis

In realtà c’è solo una regola che regola la distribuzione degli elementi sul main axis, sia orizzontale che verticale: justify-content. Questa proprietà dispone gli elementi secondo un preciso ordine, ripartendo lo spazio rimanente di conseguenza.

Justify-content

Come dicevamo questa proprietà serve per distribuire e allineare elementi e spazio risultante lungo il main axis, orizzontale o verticale che sia. In particolare ogni valore assegnabile si basa su una regola distributiva che gestisce lo spazio tra i flex-item in modo uniforme e ordinato, senza la necessità di dover assegnare percentuali, pixel o unità di misura relative di padding o margin o altre proprietà di spaziatura.

Andiamo a vederle nello specifico una per una, prima di tutto i valori che si concentrano sul posizionamento:

  • justify-content: flex-start - è il valore di default e dispone i flex-item uno dopo l’altro a partire dal main start (tenete sempre a mente lo schema a inizio articolo) seguendo la direzione definita dalla flex-direction. La distanza tra i vari item è ddeterminata dalla proprietà gap;
  • justify-content: flex-end - dispone gli elementi seguendo il main axis ma allineandoli al main end.
  • justify-content: center - permette di centrare i flex-item rispetto al flex-container distanti ugualmente da main start e main end e con un gap tra loro.

Poi ci sono 3 valori più incentrati sulla distribuzione e sono:

  • justify-content: space-between - allinea il primo elemento al main start, l’ultimo elemento al main end e i restati flex-item li distribuisce a intervalli regolari tra i due.
  • justify-content: space-around - è un po’ più complicato da spiegare. Sostanzialmente i flex-item sono distribuiti equamente sul main axis, con lo stesso spazio attorno a loro. La parola chiave è “attorno”: significa che ogni elemento avrà a destra e sinistra lo stesso spazio. Consideriamo il solito esempio: il flex-item #1 avrà a destra e a sinistra uno spazio x, il flex-item #2 avrà allo stesso modo uno spazio x a destra e a sinistra e così via. Di conseguenza dopo il main start ci sarà uno spazio x, mentre tra un elemento e l’altro ci sarà uno spazio pari a 2x;
  • justify-content: space-evenly - distribuisce i flex items a distanza uguale sia dagli estremi che tra loro.

Nel nostro esempio iniziale aggiungiamo impostiamo la width del container a 500px. Vediamo con il solito schema cosa succede variando il valore di justify-content, considerando il main axis orizzontale.

Figura 5 - Gli effetti di justify-content.

Allineamento: cross axis

Per l’allineamento perpendicolare ci sono invece due proprietà, ciascuna con le proprie caratteristiche. Per semplicità nei paragrafi successivi considererò il cross axis verticale.

Align-items

La prima riguarda l’allineamento perpendicolare dei singoli elementi, prendendo sempre come riferimento il cross axis.

  • align-items: stretch - è il valore di default. Questa proprietà allunga i flex-item, occupando tutta la cross size del container. In altri termini ogni elemento occuperà lo spazio che va dal cross start al cross end;
  • align-items: flex-start - allinea i flex item al cross start;
  • align-items: flex-end - allinea i flex-item al cross end;
  • align-items: center - dispone ogni flex-item al centro, ovvero a eguale distanza dal cross start e dal cross-end.

Prendiamo il nostro solito esempio di inizio articolo. Stavolta però modifichiamo la height e la settiamo su 250px. Ecco cosa succede applicando align-items:

Figura 5 - Questi sono i risultati dell'applicazione di align-items

Align-content

align-content differisce dalla precedente perché ha effetto sui container multiriga, ovvero quelli dove è stata applicato il flex-wrap: wrap. La differenza sostanziale è l’oggetto su cui agisce la proprietà: align-items opera sui singoli flex-item, mentre align-content sul “content” ovvero sul gruppo formato dai flex-item. Chiaramente gli effetti sono visibili solo se il flex-container è in qualche modo più largo o alto dell’insieme formato dai singoli elementi.

align-content può assumere questi valori:

  • align-content: normal - l’impostazione predefinita; equivale a non assegnare alcun valore;
  • align-content: flex-start - allinea il contenuto (quindi l'insieme dei flex-item) del flex-container al cross start;
  • align-content: flex-end - allinea il contenuto del flex container al cross end;
  • align-content: center - posiziona al centro della cross size il contenuto del flex container;
  • align-content: space-between - è concettualmente simile al corrispettivo jusitfy-content, cambiando solo il soggetto dell’operazione: prima riga allineata al cross start, ultima riga al cross end e le rimanenti distribuite a intervalli regolari;
  • align-content: space-around - si comporta come il justify-content: space-around, applicando lo stesso tipo di disposizione del contenuto ma applicato alle righe.;
  • align-content: space-evenly - distribuisce le righe equidistanti tra loro e gli estremi del container;
  • align-content: stretch - similmente a justify-content: stretch allunga le righe per occupare tutto lo spazio a disposizione.

Solito esempio iniziale. Ora esageriamo: aggiungiamo ben cinque flex-item e settiamo une height di 500px. I flex-item si disporranno su tre righe. Vi riporto l'ennesimo schema dei risultati:

Figura 6 - Le varie forme di align-content.

Conclusioni

Per questa prima parte direi che abbiamo aggiunto abbastanza carne al fuoco. La prossima volta approfondiremo tutte le regole che riguardano i flex-item come flex-grow e flex-shrink, cercando di andare nel dettaglio del loro funzionamento.

Mal di testa?

Questi argomenti possono creare noia e mal di testa ai non addetti ai lavori o non appassionati. Se siete stati pervasi dalla voglia di chiudere la pagina o siete arrivati subito a questa parte finale, contattatemi e penserò io al vostro sito web. Problem solved!

Alla prossima!