Sviluppo web

Caroselli con scroll-snap: tre proprietà e nessun JavaScript

scroll-snap trasforma un contenitore con overflow in un carosello che si aggancia, senza JavaScript. Le tre proprietà, i limiti e la verifica da tastiera.

In sintesi

  • Tre proprietà bastano: scroll-snap-type sul contenitore, scroll-snap-align sul figlio, scroll-padding per il primo elemento.
  • mandatory per schede di larghezza uguale, proximity quando le schede hanno misure diverse.
  • Il CSS non dà l'etichetta né il contatore: quelli restano da scrivere, e servono.

Risposta breve: scroll-snap-type più scroll-snap-align trasformano un contenitore con overflow-x: auto in un carosello che si aggancia, senza JavaScript. Il trascinamento resta nativo e la tastiera continua a funzionare.

Le tre proprietà che servono

ProprietàDoveCosa fa
scroll-snap-type: x mandatorycontenitoreaggancia sull'asse orizzontale, senza vie di mezzo
scroll-snap-align: startfigliodice dove l'elemento si ferma
scroll-padding-inlinecontenitoretiene conto del padding, così il primo elemento non resta tagliato

Il codice

.carousel {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
}

.carousel > * {
  flex: 0 0 min(80vw, 22rem);
  scroll-snap-align: start;
}

@media (prefers-reduced-motion: reduce) {
  .carousel { scroll-behavior: auto; }
}

overscroll-behavior-inline impedisce che il gesto prosegua sulla pagina quando il carosello arriva in fondo: senza, su un telefono lo scorrimento verticale si blocca. min(80vw, 22rem) dà una scheda quasi piena su telefono e fissa su desktop.

mandatory o proximity

mandatory obbliga lo scatto e va bene per schede della stessa larghezza. Quando le schede hanno misure diverse lo scatto sembra un salto: in quel caso proximity è più gentile, perché aggancia solo se sei già vicino al punto.

Il supporto è ampio dal 2019. Su Safari datato serve ancora -webkit-overflow-scrolling: touch per lo scorrimento inerziale, e la proprietà va scritta prima di overflow.

Il limite

Il carosello con scroll-snap non ha frecce, né contatore, né etichetta. Chi usa uno screen reader non sa quante schede ci sono: serve un'etichetta accessibile sul contenitore e un indicatore testuale del tipo «3 di 8». Il CSS da solo non li dà, e senza di loro il carosello è un elenco orizzontale invisibile.

Come verificarlo

  • Trascina con trackpad, touch e tastiera: lo scatto deve cadere sullo stesso elemento.
  • Controlla l'ordine di tabulazione: deve seguire l'ordine visivo, non quello del DOM se li hai separati.
  • Attiva prefers-reduced-motion nel sistema: nessuno scorrimento animato.
  • Riduci la finestra a 320 px: la prima scheda non deve restare tagliata dal padding.

Dove continuare

Nel catalogo: blocco testimonial, componenti e blocchi. Le guide: centrare un elemento e navbar su mobile; il confronto: Tailwind contro CSS Modules.

Domande frequenti

Serve ancora una libreria di caroselli?

Per lo scorrimento no. Una libreria serve se vuoi autoplay, loop infinito o frecce con stato: cose che scroll-snap non fa.

Perché lo scatto non funziona?

Quasi sempre perché il contenitore non ha un overflow dichiarato, o perché la proprietà è su un antenato. scroll-snap-type va sull'elemento che scorre.

Come nascondo la scrollbar?

Con <code>scrollbar-width: none</code> e <code>::-webkit-scrollbar</code> per i motori più vecchi. Ricorda che nascondere la barra toglie anche l'indizio che si può scorrere.

Funziona in verticale?

Sì, con <code>scroll-snap-type: y</code>. Su una pagina intera è però invasivo: la userei solo dentro un pannello con altezza fissa.

Il carosello fa male al CLS?

No, se le schede hanno un rapporto dichiarato. Il rischio è la larghezza, non l'altezza: una scheda senza misura cambia il numero di elementi visibili mentre carica.