Layout CSS e responsive design

  1. 1 Concetti di base
    1. Quale modalità di layout utilizzare?
    2. Principi di funzionamento
  2. 2 Flexbox
  3. 3 Griglia e sottogriglia
    1. Esempio di codice: griglia e sottogriglia
  4. 4 Query container
    1. Esempio di codice: tipografia fluida che utilizza unità di query container
  5. 5 Overlay nativi, posizionamento degli ancoraggi e contesti di impilamento
  6. 6 Monitoraggio dell'overflow e stabilità del layout
  7. 7 Meccanica della finestra visibile e distribuzione delle tracce
  8. 8 corsie della griglia (ovvero layout a griglia)

1 Fundamentals

Quando possibile, utilizza il motore di layout del browser per migliorare le prestazioni. Utilizza il dimensionamento intrinseco, le proprietà logiche e aspect-ratio prima di ricorrere a dimensioni impostate come hardcoded o a query supporti complicate.

1.1 Quale modalità di layout utilizzare?

Esamina l'albero decisionale dall'alto verso il basso e fermati alla prima corrispondenza. Tieni presente che i layout possono essere nidificati l'uno nell'altro e ogni decisione si basa sul caso d'uso di quel container.

  1. Si tratta di una semplice riga O colonna di elementi? Utilizza flexbox: 1D, incentrato sui contenuti, i contenuti vengono distribuiti lungo un singolo asse.
  2. Un elemento nidificato deve essere allineato alle tracce della griglia principale? Utilizza subgrid: 2D, incentrato sulle relazioni, eredita le tracce principali in modo che i nipoti possano allinearsi tra i fratelli.
  3. Si tratta di una pagina o di una struttura di componenti complessa con righe E colonne? Utilizza grid: 2D, basato sul layout, definisci lo scheletro e i contenuti lo riempiono.
  4. I contenuti sono un lungo flusso di prosa che dovrebbe essere suddiviso in colonne bilanciate? Utilizza più colonne: flusso 1D, stile giornale.
  5. Gli articoli hanno altezze diverse e devono essere imballati in modo compatto? Utilizza grid con grid-auto-flow: dense oggi; utilizza il layout a griglia nativo (noto anche come "corsie della griglia") solo quando viene fornito nel tuo target Baseline (vedi §8).
  6. Un elemento deve fluttuare sopra la pagina e rimanere legato spazialmente a un trigger, anche attraverso i confini del DOM o i contesti di impilamento? Utilizza il posizionamento di ancoraggio: anchor-name sull'attivatore, position-anchor sull'overlay (vedi §5).

1.2 Principi di funzionamento

  • Utilizza le proprietà logiche (inline-size, block-size, margin-inline, padding-block, inset-inline-start) per le dimensioni e la spaziatura del layout. Per una copertura completa, consulta CSS: Modern Architecture.
  • Applica il modello mentale "prima i contenuti" rispetto a "prima il layout": flexbox quando gli elementi determinano il flusso, griglia quando definisci prima lo scheletro.
  • Utilizza le abbreviazioni place-* (place-content, place-items, place-self) per l'allineamento su entrambi gli assi in un'unica dichiarazione.
  • Utilizza il dimensionamento intrinseco (min-content, max-content, fit-content()) e le tracce flessibili (fr, minmax()) prima di width/height fisse: meno query supporti, layout più resilienti.
  • NON utilizzare overflow: hidden per mascherare i problemi di layout, ma risolvili alla fonte. Ad esempio, alcuni elementi sostituiti come <canvas>, <img>, <svg>, <video>, <iframe> utilizzano display: inline per impostazione predefinita, il che può aggiungere uno spazio in alcuni layout. Anziché ritagliare lo spazio con overflow, rimuovilo applicando un valore display-outside di blocco (ad es. display: block, grid, flex e così via).
  • Utilizza aspect-ratio per riservare spazio ai contenuti multimediali e impedire la variazione del layout prima del caricamento degli asset.
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

Per calc-size() e il dimensionamento intrinseco basato sui vincoli, consulta Calcola con dimensioni intrinseche.

2 Flexbox

Layout monodimensionale: gli elementi scorrono lungo un unico asse principale con allineamento sull'asse incrociato. Utilizzalo per barre di navigazione, barre degli strumenti, righe di elementi e qualsiasi distribuzione di singole righe o colonne.

Cosa fare:

  • Stabilisci un contesto con display: flex e imposta l'asse principale con flex-direction (row predefinito).
  • Utilizza flex-wrap: wrap ogni volta che è possibile un overflow: nowrap senza overflow: auto/hidden si riverserà su viewport stretti.
  • Applica flex-wrap: balance dopo flex-wrap: wrap sulle righe con ritorno a capo di elementi compatti (come tag cloud, pillole di filtro, gruppi di pulsanti o link di navigazione) per distribuire gli elementi in modo uniforme tra le righe ed evitare un singolo elemento orfano nell'ultima riga; i browser senza supporto eseguono il fallback a wrap.
  • Utilizza la sintassi abbreviata flex <grow> <shrink> <basis> (ad es. flex: 1 1 250px) sugli elementi anziché impostare flex-grow/flex-shrink/flex-basis singolarmente.
  • Utilizza gap (o la forma estesa row-gap/column-gap) per la spaziatura tra gli elementi anziché i margini secondari.
  • Aggiungi il prefisso di allineamento posizionale con safe (ad es. align-items: safe center) in modo che i contenuti attivabili non vengano ritagliati quando il container è più stretto dei contenuti.
  • Spingi un singolo elemento all'estremità dell'asse principale con margin-inline-start: auto (o margin-block-start: auto): questa è la soluzione standard.
  • Esegui l'override dell'allineamento trasversale per elemento con align-self.
  • Utilizza align-items per centrare tutti gli elementi sull'asse trasversale; utilizza margin: auto su un singolo elemento per centrarlo su entrambi gli assi in modo indipendente; utilizza align-content solo quando il contenitore va a capo e ha spazio aggiuntivo tra le righe.
  • Imposta min-inline-size: 0 (o min-width: 0) sugli elementi flessibili che contengono contenuti lunghi non separabili (URL, codice, stringhe lunghe). Per impostazione predefinita, gli elementi flessibili non si riducono al di sotto delle dimensioni dei contenuti, causando un overflow.

Cosa non fare:

  • Non utilizzare justify-self sugli elementi flessibili: funziona solo su layout a griglia, a blocchi e con posizionamento assoluto. Utilizza invece i margini automatici.
  • Non utilizzare order o flex-direction: *-reverse per riordinare i contenuti interattivi. Modificano solo l'ordine visivo; l'ordine DOM determina comunque lo stato attivo sequenziale, quindi il flusso di tabulazione della tastiera non corrisponderà a ciò che l'utente vede.
  • Non confondere space-around (metà spazio alle estremità) con space-evenly (spazi uguali prima, tra e dopo).
  • Non dimenticare l'inversione degli assi: quando flex-direction: column, justify-content si allinea sull'asse del blocco e align-items si allinea sull'asse in linea, l'opposto del valore predefinito.
  • Non dimensionare sia il contenitore che i relativi elementi secondari in modo che si riempiano a vicenda: questa è una causa comune di overflow e risultati imprevisti. Assegna a un lato una dimensione definita.
  • Non impostare sia flex-basis sia width/inline-size sullo stesso elemento: flex-basis ha la precedenza in un contesto flessibile e width viene ignorato. Utilizza flex-basis (o la forma abbreviata flex) come unica fonte di dati per il dimensionamento degli elementi flessibili.
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 Griglia e sottogriglia

Layout bidimensionale: definisci righe E colonne in modo esplicito o lascia che il motore le ricavi. Subgrid consente a una griglia nidificata di ereditare le tracce della griglia principale, in modo che gli elementi secondari si allineino tra gli elementi di pari livello.

Scelta delle caratteristiche della griglia:

  • Sai esattamente di quante colonne hai bisogno?
    • Sì: utilizza canali espliciti (grid-template-columns: 200px 1fr, repeat(3, 1fr) e così via)
    • Le diverse colonne richiedono dimensioni diverse (barra laterale + principale, intestazione che si estende su tutte)? → utilizza grid-template-areas per le regioni denominate e leggibili
    • Tutte le colonne sono uniformi o posizionate esclusivamente in base al numero di riga? → utilizza repeat(N, ...) o linee con nome
    • No (responsive, numero di elementi sconosciuto): utilizza repeat(auto-fit, minmax(min, 1fr))
    • Gli elementi dell'ultima riga devono essere estesi per riempire lo spazio rimanente? → auto-fit
    • Le tracce vuote dell'ultima riga devono mantenere le dimensioni minime (preservando gli slot fantasma delle colonne)? → auto-fill
  • Devi posizionare un elemento in una posizione specifica?
    • Sì: utilizza grid-column: <start> / <end> o grid-area: <name>
    • No (si estende solo su più tracce, la posizione del flusso non è importante): utilizza grid-column: span <n>
  • Gli elementi secondari devono ereditare le dimensioni delle tracce della griglia principale (allineamento con bordi irregolari tra elementi di pari livello)?
    • Sì: utilizza la griglia secondaria sull'asse interessato
    • Il numero di elementi secondari per cella è variabile? → subgrid one axis only; use grid-auto-rows/grid-auto-columns for the other
    • Il numero di elementi secondari è fisso? → subgrid su entrambi gli assi va bene
    • No: griglia standard, non è necessaria una sottogriglia

Cosa fare:

  • Stabilisci un contesto con display: grid.
  • Utilizza grid-template-areas per layout complessi a livello di pagina: i nomi delle aree sono autoesplicativi e la dichiarazione può essere allineata in righe e colonne per una leggibilità immediata.
  • Utilizza repeat(auto-fit, minmax(200px, 1fr)) per griglie di schede adattabili che estendono le tracce riempite per riempire la riga o auto-fill per mantenere le tracce ripetute vuote alla loro dimensione minima.
  • Utilizza fr per la distribuzione proporzionale delle tracce e minmax(min, max) per le tracce flessibili ma limitate.
  • Posiziona gli elementi con grid-column: span <n> per ridimensionarli tra le tracce, grid-column: <start> / <end> per posizionarli in righe specifiche o grid-area: <name> per le regioni con nome.
  • Utilizza la griglia secondaria (grid-template-columns: subgrid o grid-template-rows: subgrid) per risolvere il problema del "bordo irregolare" negli elenchi di schede: gli elementi interni come titoli, metadati e CTA si allineano tra gli elementi di pari livello.
  • Abbina una dichiarazione subgrid a una dichiarazione grid-template-rows/-columns esplicita precedente come riserva della stessa struttura a cascata per i browser meno recenti.

Cosa non fare:

  • Non aspettarti che le dimensioni della traccia auto-fit/auto-fill provengano dai contenuti dell'elemento, ma dall'argomento delle dimensioni repeat().
  • Non utilizzare grid-auto-flow: dense sui contenuti interattivi. Dispone gli elementi in modo efficiente, ma li riordina visivamente, interrompendo il flusso di tabulazione della tastiera nell'ordine del DOM.
  • Non applicare la griglia secondaria a entrambi gli assi quando il numero di elementi secondari è variabile. Gli elementi aggiuntivi vengono inseriti nell'ultima traccia; utilizza invece grid-auto-rows/grid-auto-columns per l'asse implicito.
  • Non confondere justify-items/align-items (allinea i contenuti degli elementi all'interno della relativa traccia) con justify-content/align-content (allinea le tracce della griglia all'interno del container). L'utilizzo di quello sbagliato non ha alcun effetto.
  • Non utilizzare repeat(auto-fit/auto-fill, ...) senza un inline-size definito sul container. All'interno di display: inline-grid o di un elemento flessibile non dimensionato, il container non ha una larghezza da dividere, il che rende imprevedibile il numero di tracce.

3.1 Esempio di codice: griglia e sottogriglia

Shell della pagina: <main class="page-layout"> contiene <header>, <aside>, un <section class="card-grid"> con <div class="card"> elementi secondari e <footer>.

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 Query container

Eseguire query sulle dimensioni (o sullo stile calcolato) di un contenitore predecessore anziché dell'area visibile. Modello mentale: query contenitore = contesto del componente; query supporti = layout di pagina globale e preferenze dell'utente (prefers-color-scheme, prefers-reduced-motion).

Cosa fare:

  • Stabilisci un contesto di contenimento con container-type: inline-size (query solo sulla larghezza) o container-type: size (entrambi gli assi) su un wrapper prima che i relativi discendenti possano essere interrogati.
  • Assegna un nome ai container con container-name (o la forma abbreviata di container: container: card / inline-size) quando i contesti nidificati potrebbero entrare in conflitto.
  • Includi le unità di query container nel calcolo del tipo e della spaziatura fluidi: cqi/cqb (inline/blocco logico), cqw/cqh (fisico), cqmin/cqmax.
  • Assegna al container un block-size definito ogni volta che viene utilizzato container-type: size. In caso contrario, gli elementi discendenti vengono compressi perché il contenimento delle dimensioni forza il container a ignorare i suoi contenuti.

Cosa non fare:

  • Non utilizzare block-size come valore container-type, perché non è valido. Utilizza size per entrambi gli assi.
  • Non aspettarti che le dimensioni intrinseche degli elementi secondari influenzino il contenitore dopo aver dichiarato container-type. Una volta attivato il contenimento, il container viene calcolato come se non avesse elementi secondari.
  • Non fare affidamento sulle unità di query container all'interno dei discendenti di un predecessore non idoneo; viene eseguito il fallback all'area visibile piccola (svw/svh).

4.1 Esempio di codice: tipografia fluida che utilizza unità di query container

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

Per i pattern di stile adattabile basati sui componenti, consulta Stile basato sulle dimensioni e Scalabilità fluida.

5 Overlay nativi, posizionamento degli ancoraggi e contesti di impilamento

Quando utilizzare ciascuna primitiva di overlay:

  • Utilizza popover per UI temporanee e non modali (flyout, toast, tooltip): si trova nel livello superiore, non è necessaria la gestione di z-index.
  • Utilizza <dialog> con .showModal() per le interazioni modali che richiedono il blocco dello stato attivo e uno sfondo inerte.
  • Non combinare popover e .showModal() sullo stesso elemento: sono stati di runtime che si escludono a vicenda.

Posizionamento dell'ancoraggio (layout spaziale degli overlay):

  • Utilizza position-area (o anchor() per gli inserti) e anchor-size() per posizionare e ridimensionare un overlay rispetto al relativo trigger.
  • Utilizza position-try-fallbacks: flip-block (o flip-inline) per consentire al browser di riposizionare l'overlay quando supera l'area visibile.
  • Non combinare parole chiave fisiche e logiche in un unico valore position-area: scegli un sistema di coordinate.
  • Rileva le funzionalità con @supports (anchor-name: --x) e fornisci un'alternativa con posizione assoluta.

Per i dettagli completi sull'implementazione, le strategie di polyfill e il riferimento al valore popover, consulta Controllo dichiarativo di finestre di dialogo e popover e Tooltip sensibili alla posizione. Per il posizionamento degli ancoraggi applicato a menu a discesa, menu espandibili e indicatori di schede, consulta Menu a discesa sensibili alla visualizzazione e Sottolineatura della scheda di posizionamento degli ancoraggi.

6 Monitoraggio dell'overflow e stabilità del layout

Gestisci le variazioni del layout, le barre di scorrimento e il ritaglio in modo prevedibile.

Cosa fare:

  • Utilizza overflow: auto in modo che le barre di scorrimento vengano visualizzate solo quando i contenuti superano effettivamente i limiti.
  • Utilizza overflow: clip per ritagliare i contenuti senza stabilire un container di scorrimento; attiva lo spillover con overflow-clip-margin.
  • Utilizza scrollbar-gutter: stable per riservare spazio alle barre di scorrimento e impedire variazioni del layout quando i contenuti aumentano.
  • Utilizza overscroll-behavior: contain (o none) nelle regioni di scorrimento nidificate (come barre laterali, finestre modali ed elenchi) per impedire che le catene di scorrimento si propaghino nella pagina, oppure overscroll-behavior: chain nei wrapper di scorrimento intermedi (come le tracce di scorrimento per mostrare) quando lo scorrimento in eccesso deve propagarsi a uno scroller principale senza attivare un rimbalzo locale.
  • Utilizza la triade -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical per il troncamento su più righe. Nonostante il prefisso, questo pattern è completamente specificato e non è deprecato. Dichiara la sintassi abbreviata line-clamp senza prefisso insieme a questa; i browser che non la supportano ancora ignorano la proprietà senza problemi. Cosa non fare:

  • Non utilizzare overflow: scroll quando auto è sufficiente: scroll forza le barre di scorrimento anche quando non c'è nulla da scorrere.

  • Non utilizzare overflow: hidden quando vuoi solo ritagliare: hidden stabilisce un contenitore di scorrimento che può essere fatto scorrere in modo programmatico.

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

Per informazioni dettagliate su overflow: clip e overflow-clip-margin, consulta Controllo del ritaglio dell'overflow. Per l'handoff dello scorrimento nidificato con overscroll-behavior: chain nell'interfaccia utente di scorrimento per mostrare, consulta Scorri per mostrare. Per il colore, le dimensioni e i temi della barra di scorrimento, consulta Personalizza il colore o lo spessore di una barra di scorrimento, Modalità Buio e Adatta la barra di scorrimento alle preferenze di contrasto elevato.

7 Meccanismi della finestra visibile e distribuzione dei canali

  • Utilizza dvh/dvw per i contenitori di layout mobile che devono tenere conto dello spostamento della UI del browser (compressione/espansione della barra degli URL).
  • Non utilizzare 100vw per il layout a larghezza intera, in quanto ignora la larghezza della barra di scorrimento e causa un overflow orizzontale. Utilizza invece 100%, 100dvw o 100svw.

Per il punto di accesso del responsive design (unità viewport dinamiche, prenotazione di spazio per i contenuti multimediali, tipografia fluida), consulta Responsive design.

8 Corsie della griglia (ovvero masonry)

La specifica è in fase di sviluppo con il nome "grid lanes" (ad es. display: grid-lanes), mentre i prototipi sperimentali precedenti utilizzavano grid-template-rows: masonry.

Cosa fare:

  • Utilizza la griglia con grid-auto-flow: dense per un posizionamento compatto oggi, accettando che l'ordine DOM potrebbe non corrispondere all'ordine visivo.
  • Utilizza il layout a più colonne (columns: 3; column-gap: 1rem) per un flusso simile a una griglia con molti contenuti quando gli elementi sono frammenti di documenti anziché schede di pari importanza.
  • Considera grid-template-rows: masonry solo come un potenziamento progressivo: esegui il rilevamento delle funzionalità con @supports.

Cosa non fare:

  • Non lanciare grid-template-rows: masonry come requisito obbligatorio finché il tuo target Baseline non si aggiorna.
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}