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.
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



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-behaviorrestesmooth, 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 unaria-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: hiddenrogne visuellement. Donnez au conteneurrole="region"+aria-roledescription="carrousel"et à chaque sectionrole="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.
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) :
<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>
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éfaut | Effet |
|---|---|---|
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
container.scrollBy({ left: ±container.clientWidth }), ou un écouteur wheel qui convertit deltaY en une slide entière.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.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.