Stai costruendo una dashboard: un header, una sidebar, un’area di contenuto principale, un footer. Scegli Flexbox perché è lo strumento di layout che conosci, e venti minuti dopo ti ritrovi con tre container display: flex annidati, una sidebar che non mantiene la sua larghezza senza un numero magico, e un footer che fluttua a metà pagina quando il contenuto è poco. Niente di tutto questo è un bug di Flexbox. Stai usando uno strumento monodimensionale su un problema bidimensionale.

Flexbox distribuisce lo spazio lungo una singola linea, una riga o una colonna. Grid posiziona gli elementi su righe e colonne insieme, e le righe e le colonne sono in relazione tra loro. Gran parte della confusione tra Grid e Flexbox nasce proprio dal non cogliere questa distinzione.

Flexbox è monodimensionale, Grid è bidimensionale

Gli elementi flex vivono su un solo asse, definito da flex-direction. Con flex-direction: row ogni figlio si allinea da sinistra a destra (o da destra a sinistra, con row-reverse); con column si impilano dall’alto verso il basso. Esiste un asse trasversale, su cui agisce align-items. Ma Flexbox non fa mai allineare un elemento di una riga con un elemento di un’altra riga. Anche quando gli elementi vanno a capo, ogni riga che si forma viene disposta per conto proprio.

1
2
3
4
5
6
7
.toolbar {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}

Gli elementi Grid si collocano in righe e colonne che definisci in anticipo, e puoi posizionare e dimensionare ogni elemento rispetto a entrambi gli assi contemporaneamente.

1
2
3
4
5
6
.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
}

1fr è un’unità di frazione. Il browser sottrae prima le tracce fisse (240px), poi divide quello che resta tra le tracce fr. Quindi grid-template-columns: 1fr 2fr dà alla seconda colonna il doppio della quota della prima sullo spazio rimanente, non il doppio della sua larghezza totale.

Come costruire una navbar con Flexbox

Una navbar è il layout monodimensionale da manuale: un logo, alcuni link e un bottone su una riga, con lo spazio distribuito tra loro.

1
2
3
4
5
6
7
8
9
<nav class="navbar">
  <div class="logo">Acme</div>
  <ul class="links">
    <li><a href="/pricing">Pricing</a></li>
    <li><a href="/docs">Docs</a></li>
    <li><a href="/blog">Blog</a></li>
  </ul>
  <button class="cta">Sign up</button>
</nav>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
}

.links {
  display: flex;
  gap: 1.5rem;
  list-style: none;
}

justify-content: space-between fissa il logo al margine sinistro e il bottone a destra, con la lista dei link in mezzo. align-items: center mantiene tutti e tre centrati verticalmente anche se il logo è più alto del bottone. La lista dei link è un flex container a sé, quindi la sua spaziatura viene da gap e non tocchi mai un margin.

Riduci il viewport e niente va a capo, perché flex-wrap di default è nowrap: gli elementi si restringono fino alla loro larghezza minima di contenuto e poi escono dalla navbar. Aggiungi flex-wrap: wrap e gli elementi che non ci stanno scendono su una seconda riga. Quella seconda riga viene disposta in modo indipendente e non si allinea con nulla della prima. Per una navbar va bene. Per un layout dove le righe devono allinearsi tra loro, è il segnale che hai raggiunto il limite di quello che Flexbox può fare.

Come costruire il layout di una dashboard con CSS Grid

Le aree di griglia con nome ti permettono di leggere il layout di una pagina senza tradurre a mente numeri di riga e colonna.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 64px 1fr 48px;
  grid-template-areas:
    "sidebar header"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }

Ogni nome in grid-template-areas deve formare un rettangolo, e le stringhe sono un disegno della pagina: vedi che la sidebar occupa tutte e tre le righe guardando il CSS. La sidebar è fissa a 240px, header e footer hanno altezze esplicite, e main prende tutto quello che resta perché la sua riga è 1fr. Il footer si posiziona in fondo al viewport anche su una pagina corta, senza positioning assoluto e senza JavaScript, perché min-height: 100vh sul container più una riga 1fr bastano da sole.

La versione Flexbox della stessa pagina richiede due container: una riga flex per sidebar più contenuto, poi una colonna flex dentro il contenuto per header, main e footer, con flex: 1 su main per spingere il footer in basso. Funziona, ma stai sovrapponendo due layout monodimensionali per simulare uno bidimensionale: ogni modifica alla struttura della pagina significa toccare entrambi i container.

Una griglia di card responsive senza media query

Questo pattern da solo sostituisce un bel po’ di media query: una griglia di card che aggiunge o rimuove colonne mentre il viewport cambia.

1
2
3
4
5
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem;
}

repeat(auto-fit, minmax(220px, 1fr)) dice al browser di inserire tante colonne di almeno 220px quante ne entrano nella riga, e poi di allargarle fino a riempirla. Un container di 1400px ottiene cinque colonne di circa 260px; su un telefono resta una sola colonna a piena larghezza. Nessun breakpoint da nessuna parte.

auto-fit e auto-fill si comportano diversamente solo quando hai meno card di quante ne servirebbero per riempire una riga. auto-fit collassa le tracce vuote, quindi le card reali si allargano su tutta la riga. auto-fill mantiene le tracce vuote, quindi le card restano della larghezza che avrebbero in una riga piena e il resto della riga rimane bianco. Usa auto-fill quando la larghezza delle card deve restare costante a prescindere dal numero, come in una griglia di prodotti dove tre risultati non devono gonfiarsi a un terzo della pagina ciascuno. Usa auto-fit quando una riga mezza vuota sembra peggio di card larghe, come in una dashboard con due o tre tile di riepilogo.

Come usare Grid e Flexbox insieme

Raramente scegli l’uno o l’altro per un’intera pagina. Grid gestisce lo scheletro; Flexbox gestisce il contenuto monodimensionale dentro ciascuna regione.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

Ogni .card si trova in una cella della griglia e si allunga fino all’altezza della cella, perché per default gli elementi grid si allungano a riempirla. Al suo interno, header, body e footer formano un’unica colonna, quindi justify-content: space-between porta il footer in fondo a ogni card, indipendentemente da quanto testo ha il body di ciascuna. Questa è la regola pratica: Grid per tutto ciò che deve mettere in relazione una riga e una colonna insieme, Flexbox per il contenuto che scorre lungo una sola linea, annidato nel punto in cui Grid lo ha collocato.

Se usi Grid per passare da una vista a card a una vista a lista, salva la scelta del visitatore in localStorage, come spiegato in LocalStorage, SessionStorage o Cookie: Cosa Usare. È una sola chiamata a setItem, e la scelta sopravvive ai reload e alle tab chiuse senza un giro di andata e ritorno verso il server.

Quando usare subgrid per le griglie annidate

Una grid dentro un elemento di un’altra grid genera un proprio insieme di tracce, indipendente da quello del genitore. Questo si rompe appena vuoi che le righe interne di una card (immagine, titolo, descrizione, prezzo) si allineino lungo una riga di card. Imposta grid-template-rows: subgrid (o grid-template-columns: subgrid) sulla grid annidata e questa riusa le tracce del genitore invece di definirne di nuove, così la riga del titolo di ogni card si allinea con quella delle vicine anche se ogni card resta un grid container a sé. Subgrid funziona in tutti i principali browser moderni, quindi puoi usarlo direttamente invece di forzare altezze uguali con valori fissi.

Quando Flexbox è la scelta migliore anche se Grid potrebbe farlo

Grid può disporre una singola riga con grid-auto-flow: column. Per contenuto davvero monodimensionale, però, Flexbox ha qualcosa che Grid non ha: flex-grow e flex-shrink permettono a ogni elemento di rivendicare più o meno spazio residuo in base al proprio contenuto. È quello che vuoi per una barra di ricerca che deve occupare tutto lo spazio lasciato libero da un bottone e un’icona a larghezza fissa. Usa Grid di default quando righe e colonne devono essere in relazione tra loro; usa Flexbox quando stai disponendo una sola riga e vuoi grow e shrink per singolo elemento, non solo l’allineamento.

L’errore funziona anche al contrario. Capita di costruire un layout a più colonne con flex-wrap: wrap più una width in percentuale su ogni elemento, per simulare una grid. Tiene finché l’ultima riga ha meno elementi delle altre. A quel punto space-between spinge gli ultimi due elementi ai lati opposti, oppure flex-grow li allarga più di tutto quello che sta sopra. Il browser continua a distribuire elementi lungo delle linee, non a riempire una griglia. Se stai contando le colonne, usa Grid.

Se hai ancora bisogno di JavaScript sul resize, per esempio per riposizionare un tooltip vicino a una card, applica il debounce all’handler come spiegato in Debounce vs Throttle in JavaScript: Quando Usare Ciascuno invece di eseguirlo a ogni evento di resize scatenato dal browser.

Grid o Flexbox: come scegliere

Una sola domanda decide tutto: gli elementi hanno righe e colonne che devono allinearsi tra loro, oppure è una singola riga di elementi?

SituazioneUsa
Navbar, gruppo di bottoni, toolbarFlexbox
Scheletro della pagina: header, sidebar, main, footerGrid
Griglia responsive di card o prodottiGrid (auto-fit/auto-fill + minmax)
Centrare un elemento, o distribuire uniformemente pochi elementiFlexbox
Layout di un form con label e input allineatiGrid
Contenuto dentro una singola cella grid (header/body/footer di una card)Flexbox, annidato dentro la grid

La prossima volta che apri un foglio di stile per un nuovo layout, disegna prima i box. Se tracci linee che si incrociano, è Grid. Se ogni box sta su una sola riga, è Flexbox.

Articoli correlati