Scroll✨ Premium

Horizontal Snap

Cinq sections en min-width: 100 % dans un conteneur de 280 px en overflow: auto hidden et scroll-snap-type: x mandatory : chaque slide occupe toute la largeur et masque ses voisines, sans une ligne de JavaScript. Mesuré : l'amplitude d'un cran de molette dépend du navigateur — une slide dans Firefox et Safari, retour à zéro dans Chrome tant que le geste n'atteint pas la moitié de la largeur.

CSSSnapHorizontal

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →

3 exemples d'usage

Comparatif d'offres cloud pour grandes entreprises — une offre par écran — Horizontal Snap exemple 1

① Comparatif d'offres cloud pour grandes entreprises — une offre par écran

QuandPage produit d'un hébergeur cloud : Starter, Pro, Business, Enterprise, chacune sur une slide pleine largeur avec prix, trois garanties et un bouton ; flèches et points de position sous le bloc.
PourquoiChaque offre occupe la totalité de l'espace : min-width: 100% et scroll-snap-align: start font qu'aucun bord de l'offre voisine n'est visible — Card Snap Carousel (fx-0595) laisserait dépasser l'offre suivante et amorcerait la comparaison avant la lecture. Le prospect lit un plan, puis passe au suivant d'un geste ou d'un clic.
RéglagesQuatre sections, hauteur portée à 270 px, dégradés remplacés par une teinte par offre, barre de défilement masquée (scrollbar-width: none) et remplacée par des flèches qui appellent scrollTo({ left: i × clientWidth }) — nécessaire dans Chrome, où un cran de molette ne suffit pas —, tabindex="0" et aria-label sur le conteneur.
Visite guidée des espaces d'un hôtel-boutique — un espace par balayage — Horizontal Snap exemple 2

② Visite guidée des espaces d'un hôtel-boutique — un espace par balayage

QuandSur mobile, la suite, le restaurant et le spa : une photo plein cadre, un titre et deux lignes par slide, le balayage révèle l'espace suivant.
PourquoiL'immersion tient au cadre entier : rien de l'espace suivant ne dépasse, contrairement à Horizontal Scroll (fx-0541) dont les vignettes de 180 px se devinent en bord. Le balayage est natif (aucun écouteur tactile), l'accroche mandatory garantit qu'on ne s'arrête jamais entre deux photos.
RéglagesTrois sections, hauteur = celle de l'écran du téléphone (324 px), dégradés remplacés par des « photos » en dégradés superposés, contenu aligné en bas à gauche (justify-content: flex-end), texte à rgba(255,255,255,.85), points de position mis à jour sur scroll, avance automatique toutes les 1,1 s coupée sous prefers-reduced-motion.
Tutoriel premiers pas d'une application mobile — cinq écrans, un geste — Horizontal Snap exemple 3

③ Tutoriel premiers pas d'une application mobile — cinq écrans, un geste

QuandOnboarding d'une application : Créez votre compte, Invitez votre équipe, Importez vos données, Configurez les alertes, C'est parti ! — cinq écrans plein cadre, un bouton Suivant et des points.
PourquoiLe balayage horizontal est le geste natif de l'onboarding mobile ; Vertical Snap (fx-0593) imposerait un défilement vers le bas qui se lit comme un retour en arrière dans une application. Le bouton Suivant fait le même travail que le doigt (scrollTo d'une largeur) et l'accroche recale l'écran au pixel.
RéglagesCinq sections sur fond clair (dégradés retirés, background: #fff, titre #0f172a, texte #475569), hauteur 236 px, scroll-snap-type: x mandatory conservé, bouton « Suivant » branché sur scrollTo({ left: i × clientWidth }), liste des étapes synchronisée sur scroll.

Comment ça marche

Deux classes font tout. .sn-hsnap-container : width: 100%, height: 280px, overflow: auto hidden (défilement horizontal, vertical coupé), scroll-snap-type: x mandatory, scroll-behavior: smooth, display: flex. Dedans, cinq .sn-hsnap-section : min-width: 100% et flex-shrink: 0 — chaque section mesure exactement la largeur du conteneur, jamais moins —, height: 280px, scroll-snap-align: start, colonne flex centrée (gap: 8px, padding: 20px), un h4 de 0,9 rem (14,4 px) et un p de 0,72 rem (11,5 px) à 60 % de blanc, et un dégradé à 135° par :nth-child(1) à (5), de #0f172a → #1e293b jusqu'à #64748b → #94a3b8. Mesuré dans une zone de 1 280 px : cinq sections de 1 280 px, scrollWidth 6 400 px, aucune erreur console, js vide — l'effet est du CSS pur. Le reste du HTML vendu est du décor de catalogue : l'enveloppe .demo-preview (flex centré, min-height: 300px), l'indication .sdc-scroll-hint « Scrollez → » (10,4 px à 30 % de blanc, pulsation sdcHintPulse de 2 s en boucle, posée en absolu à 8 px du bas de l'enveloppe, donc à cheval sur le bord bas du conteneur) et cinq style="font-family: Inter…" en ligne sur les titres.

Le mécanisme est le scroll snap natif : mandatory oblige le navigateur, à la fin de chaque geste, à se poser sur un point d'accroche — le bord de départ d'une section, soit un multiple de la largeur. scroll-behavior: smooth ne concerne que les défilements programmés (scrollTo, ancres) ; la molette, le clavier et le doigt sont lissés par le navigateur, pas par le code. Et c'est là que les moteurs divergent, mesuré sur une slide de 1 280 px. Chrome 153 (fenêtré, lissage réel) : un cran de molette de 120 px avance à 118 px puis revient à 0 en 170 ms ; 600 px reviennent aussi ; 700 px passent à la slide 2 ; trois crans espacés de 60 ms retombent à 0 — le seuil est la moitié de la largeur (640 px), point d'accroche le plus proche de la fin du geste. Un balayage tactile synthétisé de 220 px revient à 0, 700 px avancent d'une slide. Firefox : tout événement de molette vaut exactement une slide (30, 120 ou 3 000 px → 1 280), trois crans → trois slides. WebKit : un cran → une slide en un fondu mesuré à 1 s ; 3 000 px d'un coup → trois slides. Dans les trois, la molette verticale seule donne 0 : une souris classique ne bouge jamais les slides sans Maj (conversion faite par le navigateur, pas par le code).

Trois contraintes tiennent aux dimensions. Les deux hauteurs fixes (280 px) et overflow-y: hidden : une slide dont le contenu dépasse 240 px (280 − 2 × 20 de padding) est rognée en haut et en bas, sans défilement vertical possible — mesuré avec 30 lignes : le paragraphe s'étend de −42 px à +348 px dans une section de 280 px, les lignes 1 à 3 et 25 à 30 sont invisibles. La barre de défilement classique (Windows, Linux, macOS réglé sur « toujours ») : 15 px pris dans les 280 px du conteneur (clientHeight 265) alors que les sections gardent 280 — le bas de chaque slide passe sous la barre. La fin de course : à la slide 5 (scrollLeft 5 120), un cran de plus ne fait rien, pas de retour au début. Enfin le clavier : Chrome 153 met le conteneur dans l'ordre de tabulation (scrollers focalisables) et → avance d'une slide par appui en 830 ms ; Firefox, WebKit et le Chromium headless passent le conteneur sans s'y arrêter — un tabindex="0" règle la question ; Début et Fin agissent sur l'axe vertical et ne font rien ici.

Accessibilité

  • prefers-reduced-motion absent du code. Mesuré sous préférence émulée (Chrome 153) : scroll-behavior reste smooth, l'accroche au clavier dure toujours 830 ms, la pulsation de l'indication tourne toujours (1 animation active). Ajoutez @media (prefers-reduced-motion: reduce) { .sn-hsnap-container { scroll-behavior: auto } .sdc-scroll-hint { animation: none } } — le fondu de la molette et du doigt, lui, est celui du navigateur et suit le réglage du système, pas votre CSS.
  • Clavier : le conteneur n'est dans l'ordre de tabulation que dans Chrome récent (mesuré : Tab s'y arrête dans Chrome 153, le saute dans Firefox, WebKit et le Chromium headless). Posez tabindex="0", role="region" et un aria-label ; ← → avancent alors d'une slide par appui (mesuré 1 280 → 2 560 → 3 840). Début et Fin n'agissent pas sur l'axe horizontal, et une souris à molette verticale n'agit pas du tout : prévoyez des boutons précédent/suivant visibles.
  • Contraste mesuré sur les pixels (Chrome, centre des slides) : slides 1 et 2 correctes (titre blanc ≥ 14:1, texte à 60 % ≥ 4,9:1) ; slide 3, texte 4,46:1 (juste sous 4,5) ; slide 4, texte 3,39:1 ; slide 5, titre 3,56:1 et texte 2,27:1. Les deux dégradés les plus clairs échouent pour un gras de 14,4 px et un texte de 11,5 px. Passez le paragraphe à rgba(255,255,255,.85) ou arrêtez les dégradés à #475569 → #64748b. L'indication « Scrollez → » : 10,4 px à 2,6:1 — décor à retirer.
  • Lecteurs d'écran : les cinq sections sont du contenu natif, rien n'est masqué à la synthèse vocale — les cinq titres sont lus à la suite quelle que soit la slide affichée, y compris ce que overflow-y: hidden rogne visuellement. Donnez au conteneur role="region" + aria-roledescription="carrousel" et à chaque section role="group" + aria-label="Slide 2 sur 5". Aucune zone live n'existe : sans JavaScript, le changement de slide n'est jamais annoncé.

Compatibilité navigateur

CSS pur, sans animation-timeline : overflow à deux valeurs (auto hidden), scroll-snap-type: x mandatory, scroll-snap-align, scroll-behavior: smooth, gap en flex et linear-gradient. Le défilement est natif : Firefox le gère, contrairement aux animations pilotées au scroll en CSS (animation-timeline) qu'il ne supporte dans aucune version. Aucune dépendance.

Chrome 84+✓ Complet (69–83 : sans le gap de 8 px)
Firefox 99+✓ Complet (68–98 : accroche jugée partielle par MDN)
Safari 15.4+✓ Complet (13.1–15.3 : scrollTo sans lissage)
Edge 84+✓ Complet
Mobile iOS 15.4+✓ Complet (balayage natif)
Android Chrome 84+✓ Complet (balayage natif)

Sans la syntaxe overflow à deux valeurs (Chrome < 68, Firefox < 61, Safari < 13.1), la déclaration entière est ignorée : overflow: visible, plus de conteneur de défilement — les cinq sections débordent de la page vers la droite et rien n'accroche. Écrivez overflow-x: auto; overflow-y: hidden en deux propriétés pour couvrir ces versions. Sans scroll-behavior (Safari < 15.4), les défilements programmés sont instantanés ; sans gap flex (Safari < 14.1), titre et texte se touchent. Sans JavaScript : rien ne change, il n'y en a pas.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="sn-hsnap-container">
  <div class="sn-hsnap-section">
    <h4>Slide 1</h4>
    <p>Faites glisser horizontalement</p>
  </div>
  <div class="sn-hsnap-section">
    <h4>Slide 2</h4>
    <p>Navigation intuitive</p>
  </div>
  <div class="sn-hsnap-section">
    <h4>Slide 3</h4>
    <p>Parfait pour les galeries</p>
  </div>
  <div class="sn-hsnap-section">
    <h4>Slide 4</h4>
    <p>Experience mobile optimisee</p>
  </div>
  <div class="sn-hsnap-section">
    <h4>Slide 5</h4>
    <p>Dernière diapositive</p>
  </div>
</div>
<span class="sdc-scroll-hint">Scrollez →</span>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Nombre de slides (HTML — blocs .sn-hsnap-section) 5 Ajoutez ou retirez des sections ; la largeur de défilement suit (mesuré 5 × largeur). Les dégradés ne sont définis que pour :nth-child(1) à (5) : une sixième slide n'a pas de fond.
Hauteur (CSS — height de .sn-hsnap-container et de .sn-hsnap-section) 280 px / 280 px Gardez les deux égales. 100dvh sur les deux pour des slides plein écran. Le contenu qui dépasse hauteur − 40 px est rogné (overflow-y: hidden), et une barre de défilement classique prend 15 px dans cette hauteur.
Largeur des slides (CSS — min-width: 100%) 100 % C'est ce qui masque les voisines. À 85%, le bord de la suivante apparaît et l'accroche start le laisse dépasser à droite : vous êtes alors sur le territoire de fx-0595, qui ajoute gap et scroll-snap-align: center.
scroll-snap-type (CSS — .sn-hsnap-container) x mandatory x proximity laisse l'utilisateur s'arrêter entre deux slides si son geste finit loin d'un point d'accroche ; mandatory l'interdit. Le seuil de Chrome (moitié de la largeur) ne change pas.
scroll-behavior (CSS — .sn-hsnap-container) smooth N'agit que sur scrollTo, scrollBy et les ancres — vos boutons précédent/suivant. auto sous prefers-reduced-motion.
Dégradés (CSS — :nth-child(1) à (5)) #0f172a → #94a3b8, 135° Une paire de couleurs par slide. Les deux dernières sont trop claires pour le texte vendu (mesuré 3,56:1 et 2,27:1) : arrêtez la rampe à #64748b ou éclaircissez le paragraphe.
Textes (CSS — h4 0,9 rem, p 0,72 rem à 60 % de blanc) 14,4 px / 11,5 px Tailles de vignette de catalogue. Pour une slide plein écran, clamp(1.4rem, 3vw, 2.4rem) pour le titre et 1 rem pour le texte à rgba(255,255,255,.85).
overflow (CSS — .sn-hsnap-container) auto hidden Raccourci à deux valeurs (x puis y). Écrivez overflow-x: auto; overflow-y: hidden pour Safari < 13.1 ; overflow-y: auto permet de faire défiler une slide trop haute, au prix d'un défilement imbriqué au doigt.

FAQ

Parce que Chrome choisit, à la fin du geste, le point d'accroche le plus proche de la position atteinte : sur une slide de 1 280 px, un cran de 120 px arrive à 118 px, et 0 est plus proche que 1 280 — retour au départ, mesuré en 170 ms dans Chrome 153 fenêtré. Il faut dépasser la moitié de la largeur en un seul geste : 700 px passent, 600 px reviennent, trois crans espacés de 60 ms reviennent aussi. Firefox avance d'une slide par événement de molette, WebKit aussi (avec un fondu d'environ 1 s), quel que soit le delta. Et dans les trois, une molette verticale seule ne fait rien — il faut Maj + molette, un pavé tactile ou un doigt. Le correctif n'est pas dans le CSS : deux boutons qui appellent container.scrollBy({ left: ±container.clientWidth }), ou un écouteur wheel qui convertit deltaY en une slide entière.
Le conteneur est le même dans les trois — overflow: auto hidden, scroll-snap-type: x mandatory, flex — ; tout se joue sur la largeur des enfants. Ici min-width: 100% et scroll-snap-align: start : une slide remplit le cadre et aucune voisine ne dépasse. fx-0595 pose des cartes de 200 × 240 px avec gap: 12px et scroll-snap-align: center, fx-0541 des vignettes de 180 × 140 px centrées : les voisines se voient en bord, ce qui invite à balayer mais divise l'attention. Conséquence mesurable du plein cadre : le seuil de Chrome vaut la moitié d'une slide, 640 px ici contre une centaine de pixels sur une carte de 200 px — d'où les boutons de la question précédente. Choisissez celui-ci quand chaque écran doit être lu seul : une offre, une photo, une étape.
Donnez height: 100dvh au conteneur et aux sections (les deux ensemble : la section vendue a sa propre hauteur de 280 px). Tout ce qui dépasse hauteur − 40 px est perdu : le conteneur est en overflow-y: hidden et la section centre son contenu, donc le rognage se fait en haut et en bas — mesuré avec 30 lignes dans une section de 280 px : le texte va de −42 px à +348 px, lignes 1 à 3 et 25 à 30 invisibles, scrollHeight 348 sans aucun moyen de défiler. Pour une offre à dix garanties, réduisez le contenu ou passez la section en overflow-y: auto avec height: 100% — en sachant qu'un défilement vertical dans un conteneur à accroche horizontale gêne le balayage au doigt. Sur Windows, réservez 15 px en bas ou masquez la barre (scrollbar-width: none) en fournissant des boutons.