DEV Community

frontendfacile.it
frontendfacile.it

Posted on • Originally published at frontendfacile.it

3 pattern CSS per layout moderni (più due “boost” estetici)

Scroller orizzontali senza JS, griglie che non vanno in overflow e componenti davvero adattivi con le container query.

I pattern di layout più utili sono spesso quelli che ti risolvono problemi ricorrenti con poche righe, senza introdurre complessità (o JavaScript) dove non serve. Qui raccolgo tre soluzioni che uso spesso in progetti reali, con un paio di “upgrade” estetici/funzionali che puoi applicare come progressive enhancement.


1) Overflow scroller a card con CSS Grid (e scroll snapping)

Lo scenario classico: una lista di card orizzontale che su mobile si sfoglia naturalmente, e su desktop deve restare confinata (senza far scorrere l’intera pagina).

Base: Grid che “scorre” in colonne

Puoi farlo con Flexbox, ma con Grid spesso è più comodo perché il contenitore governa dimensioni e flusso in modo molto prevedibile.

.scroller {
  display: grid;
  gap: 1rem;

  /* invece di riempire righe, crea colonne */
  grid-auto-flow: column;

  /* larghezza “standard” di ogni card/colonna */
  grid-auto-columns: 300px;

  /* lo scorrimento resta nel componente */
  overflow-x: auto;
}
Enter fullscreen mode Exit fullscreen mode
  • grid-auto-flow: column è la chiave: cambia il flusso e ti mette gli item “in fila”.
  • grid-auto-columns ti evita card con larghezze imprevedibili.
  • overflow-x: auto (o scroll) confina lo scroll nell’elemento.

Upgrade: scroll-snap per un feeling più “UI”

Lo snapping è perfetto per card orizzontali: rende lo scroll più controllato, soprattutto su touch.

.scroller {
  scroll-snap-type: x mandatory;
}

.scroller > * {
  scroll-snap-align: center; /* oppure start/end */
}
Enter fullscreen mode Exit fullscreen mode

Note pratiche:

  • mandatory è più “deciso” (snappa sempre). proximity è più permissivo.
  • Lo snap non funziona se definisci solo scroll-snap-type: serve anche scroll-snap-align sui figli.

2) Auto-grid responsive con repeat(auto-fit, minmax())… senza overflow

Il pattern “auto-grid” è comune: colonne che si aggiungono/tolgono automaticamente e si adattano alla larghezza disponibile.

.auto-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
Enter fullscreen mode Exit fullscreen mode

Funziona bene… finché non alzi troppo il minimo. Se imposti minmax(400px, 1fr) e il viewport (o il contenitore) scende sotto 400px, rischi overflow orizzontale.

La correzione robusta: min() dentro il minimo

L’idea è: “non scendere sotto 400px se possibile, ma se il contenitore è più stretto allora usa 100%”.

.auto-grid {
  --min-col: 300px;

  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, var(--min-col)), 1fr)
  );
}
Enter fullscreen mode Exit fullscreen mode

Perché funziona:

  • min(100%, var(--min-col)) sceglie il valore più piccolo.
  • Se il contenitore è stretto, il minimo diventa 100% → niente overflow.
  • Appena c’è spazio, torna a rispettare --min-col.

È una dichiarazione lunga, ma è un pattern “scrivi una volta, riusa ovunque”: cambi solo --min-col.


3) Layout davvero adattivi con Container Queries (perfetti per sidebar)

Ci sono componenti che cambiano “personalità” non in base al viewport, ma in base a dove li metti.

Esempio tipico:

  • Lo stesso blocco (es. lista di promo) in sidebar è stretto → layout in colonna.
  • In una sezione centrale può diventare largo → meglio 3 colonne.
  • Riducendo il viewport, quell’area può allargarsi o stringersi in modi non lineari.

Con le media query diventa un incastro di breakpoint. Con le container query è diretto: il componente reagisce alla larghezza del suo contenitore.

1) Definisci un container

Sul wrapper più sensato (spesso l’aside o la sezione che contiene il componente):

.sidebar {
  container-type: inline-size;
}
Enter fullscreen mode Exit fullscreen mode

2) Applica la regola al componente quando il container supera una soglia

@container (width > 500px) {
  .promo-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
  }
}
Enter fullscreen mode Exit fullscreen mode

Risultato: lo stesso markup è riusabile e “intelligente” in contesti diversi, senza dipendere dal viewport.


Bonus 1) Angoli “scooped” con corner-shape (progressive enhancement)

Se vuoi dare un look da “ticket/coupon” a card o pill, puoi combinare border-radius con corner-shape.

.promo-list > li {
  border-radius: 8px;
  corner-shape: scoop;
}
Enter fullscreen mode Exit fullscreen mode

Dove non supportato, resta semplicemente il border-radius. Dove supportato, ottieni lo “scavo” agli angoli.


Bonus 2) Rendere lo scroller più “soddisfacente” con animazioni guidate dallo scroll

Un’idea carina per gli scroller orizzontali: far sì che le card in evidenza risultino più grandi/leggibili, mentre quelle ai lati siano leggermente ridotte e più trasparenti.

Un’impostazione di base (keyframe) può essere:

@keyframes scroller-focus {
  0%, 100% {
    opacity: .5;
    transform: scale(.9);
  }
  35%, 65% {
    opacity: 1;
    transform: scale(1);
  }
}
Enter fullscreen mode Exit fullscreen mode

Questo tipo di effetto dà il meglio con animazioni legate allo scroll (progressive enhancement, perché supporto e dettagli dipendono dalle feature di scroll-driven animations). Se lo applichi, fallo con attenzione: deve migliorare la percezione, non distrarre.


In sintesi

  • Scroller orizzontale: Grid + grid-auto-flow: column + grid-auto-columns + overflow-x.
  • Auto-grid solida: minmax(min(100%, --min-col), 1fr) per evitare overflow sui contenitori stretti.
  • Componenti adattivi: container queries per layout che dipendono dal contesto (sidebar vs main).
  • Finiture: corner-shape e animazioni scroll-driven come miglioramenti opzionali.

Se vuoi, posso prepararti una mini demo completa (HTML + CSS) con tutti e tre i pattern, pronta da copiare in un CodePen/StackBlitz.


Articolo originale: https://frontendfacile.it/blog/3-pattern-css-per-layout-moderni-piu-due-boost-estetici

Top comments (0)