Scroll✨ Premium

Horizontal Scroll

Six vignettes de 180 × 140 px sur une piste de 1 172 px, dans un conteneur overflow: auto hidden à scroll-snap-type: x mandatory : chaque geste s'arrête sur une vignette centrée, les voisines dépassent des deux bords — 1,4 Ko de CSS, zéro JavaScript, aucune animation : le doigt ou la molette donnent la vitesse. Une racine .sb-horiz porte le fond sombre et ancre l'indice ; le conteneur est borné à 900 px pour que la piste dépasse toujours, même sur un grand écran.

CSSHorizontalSnap

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

Nuancier de destinations — application mobile d'une agence de voyage — Horizontal Scroll exemple 1

① Nuancier de destinations — application mobile d'une agence de voyage

QuandÉcran d'accueil d'une application de voyage : un titre, un filtre, et une rangée de destinations à faire défiler au doigt, chaque vignette portant la ville, le pays et un prix d'appel.
PourquoiLes destinations voisines restent partiellement visibles des deux côtés : Paris à gauche, Bangkok à droite, Tokyo devant. C'est ce « peeking » qui invite à comparer d'un coup d'œil et signale qu'il y en a d'autres. Horizontal Snap (fx-0594) occupe 100 % de la largeur par diapositive et cache tout sauf la destination active ; ici le doigt règle le rythme et le snap pose chaque ville au centre.
RéglagesLa racine .sb-horiz est la zone de 291 px sous le titre (la scène garde son propre fond), conteneur en height: 100% ; vignettes 220 × 230 px, arrondi 18 px, texte en bas sur un voile linear-gradient(180deg, transparent 45%, rgba(0,0,0,.62)) ; piste padding: 8px 16px 26px, gap: 12px. À 540 px de large, 148 px de chaque voisine restent visibles ; arrêts mesurés 0, 88, 320, 552, 784, 872.
Bande d'imageries chronologiques — application de radiologie sur tablette — Horizontal Scroll exemple 2

② Bande d'imageries chronologiques — application de radiologie sur tablette

QuandDossier d'un patient sur tablette : une barre d'identification (patient, séquence), puis la série d'examens classés par date, chaque image accompagnée de sa date et de son motif.
PourquoiLes images adjacentes servent de repères temporels : en centrant le contrôle d'octobre, le praticien voit encore le bord de l'examen précédent et du suivant. Les largeurs peuvent différer d'une acquisition à l'autre (170 à 260 px ici) : scroll-snap-align: center centre chacune quelle que soit sa taille, là où Card Snap Carousel (fx-0595) impose des cartes de 200 px avec une structure image-titre-texte.
RéglagesCinq figure de largeurs 190, 240, 170, 260 et 200 px sur 240 px de haut, arrondi 8 px, bordure 1 px rgba(148,163,184,.22), légende sur fond #0e1628 ; piste padding: 26px 16px 22px sous un axe 2024 → 2026 ; police à chasse fixe ; conteneur 540/1 140 px, course 600 px.
Sélecteur de matières — borne tactile d'un showroom de cuisines — Horizontal Scroll exemple 3

③ Sélecteur de matières — borne tactile d'un showroom de cuisines

QuandÉtape « Façades » d'un configurateur sur borne : interface claire, un rappel de la sélection en cours, et un nuancier de matières à faire glisser (chêne, béton ciré, laqué, noir mat, noyer, terrazzo) avec code article et supplément.
PourquoiLe défilement est libre : le client compare deux matières côte à côte le temps qu'il veut et lit les codes. Sticky Horizontal (fx-0578) ferait défiler tout seul, sur une animation de 12 s, sans laisser le temps de lire ; ici la matière voisine dépasse du bord et signale la suite du nuancier, et le snap aligne toujours une matière au centre de la borne.
RéglagesThème clair : scène #f3f1ec, vignettes 186 × 236 px blanches, bordure #e2ddd3, ombre 0 6px 18px rgba(60,50,30,.10), échantillon de 160 px en dégradés CSS (bois en repeating-linear-gradient), matière courante en outline: 3px solid ; l'indice « Scrollez → » recoloré rgba(28,27,25,.55) pour rester visible sur fond clair.

Comment ça marche

Une racine, trois niveaux et une étiquette. .sb-horiz est la racine de l'effet : position: relative (l'ancre de l'indice), width: 100%, fond #0a0a0f, texte blanc, police sans-serif — collée dans une page blanche, elle apporte son propre fond au lieu de compter sur celui de la page. Dedans, .sb-horiz-content est le conteneur défilant : width: 100%, max-width: 900px, margin: 0 auto, height: 280px, overflow: auto hidden (défilement horizontal, rien en vertical), scroll-snap-type: x mandatory, display: flex et align-items: center. Il contient .sb-horiz-track, une piste flex de gap: 12px et padding: 20px 16px, qui aligne six .sb-horiz-item de 180 × 140 px (flex-shrink: 0, arrondi 14 px, chiffre blanc gras en 1,2 rem, scroll-snap-align: center). Chacune porte son dégradé à 135° en attribut style, et la chaîne se referme sur elle-même : la couleur de fin d'une vignette est celle du début de la suivante (#6366f1 → #8b5cf6 → #d946ef → #f43f5e → #f59e0b → #22c55e → #06b6d4). Piste : 6 × 180 + 5 × 12 + 2 × 16 = 1 172 px. Sous le tout, .sdc-scroll-hint « Scrollez → » : absolu dans la racine, 8 px du bas, 0,65 rem, blanc à 30 %, dont l'opacité oscille de .3 à .7 toutes les 2 s (sdcHintPulse). Le champ JavaScript est vide : la mécanique entière est celle du navigateur.

Le snap. À la fin de chaque geste, le navigateur pose le défilement sur une position d'arrêt : une vignette centrée dans le conteneur (center). Les positions hors course sont rabattues sur les bords. Mesuré dans Chromium à 728 px de conteneur (page de 744 px) : course de 444 px et quatre arrêts seulement pour six vignettes — 0, 126, 318, 444 — ; seules la 3 et la 4 se centrent, les 1-2 et 5-6 ne sont visibles qu'aux butées, par paires. À 374 px (mobile) : course de 798 px, arrêts 0, 111, 303, 495, 687, 798, les vignettes 2 à 5 se centrent. À 900 px, la borne du conteneur sur un écran de bureau : course de 272 px, arrêts 0, 40, 232, 272 (scrollLeft = 100 se lit 40). Un cran de molette horizontale (100 unités) déplace de 117 px puis se pose à 126. Chromium snappe aussi les écritures programmatiques : scrollLeft = 100 se lit 126, 50 se lit 0 — impossible de laisser le carousel « entre deux ». Au doigt, chaque glissement de 150 px emmène à la vignette suivante (111 → 303 → 495 → 687) et un geste rapide de 300 px file jusqu'à la butée (798) : mandatory impose l'arrêt, pas la vitesse. Pas de scroll-behavior : un scrollTo() est instantané sauf à passer behavior: 'smooth' (≈ 230 ms pour 192 px dans Chromium).

Pourquoi une racine et une borne. L'indice est en position: absolute : il se cale sur l'ancêtre positionné le plus proche, et sans .sb-horiz ce serait la fenêtre (mesuré sur une version sans racine : indice à y = 579 dans une fenêtre de 600 px, le carousel finissant à y = 408). La racine le garde à 8 px du bas du carousel, où que le code soit collé, et elle porte le fond sombre sur lequel ses 30 % de blanc restent visibles. La borne, elle, tient à une propriété du défilement : un conteneur ne défile que si son contenu est plus long que lui. Sans max-width, un conteneur en width: 100% fait 1 264 px sur une page de 1 280 : la piste de 1 172 px y tient entière, scrollWidth = clientWidth, course nulle, rien ne défile (mesuré). Bornée à 900 px, la course est de 272 px sur cette même page ; dans une colonne déjà plus étroite (744 px de l'aperçu du catalogue, 374 px d'un mobile), la borne ne joue pas. Enfin, la molette verticale ne fait rien (0 px après trois crans de 120) : un conteneur horizontal n'écoute que son axe, et c'est voulu — détourner la molette verticale bloquerait le défilement de la page au passage du carousel. À la souris, il faut Maj + molette, une molette latérale, la barre de défilement ou le clavier ; pavé tactile et doigt défilent nativement.

Accessibilité

  • prefers-reduced-motion absent du code. La seule animation est la pulsation d'opacité de l'indice (.3 ↔ .7, 2 s, infinie) ; mesuré sous reduce, sdcHintPulse tourne toujours. Le snap lui-même est un défilement du navigateur, pas une animation, et sans scroll-behavior: smooth il n'y a rien d'autre à neutraliser. Ajoutez @media (prefers-reduced-motion: reduce) { .sdc-scroll-hint { animation: none } }.
  • Clavier. Mesuré dans Chromium : Tab donne le focus au conteneur (les zones défilantes sans enfant focalisable sont focalisables depuis Chrome 130, et depuis longtemps dans Firefox), Flèche droite avance d'un arrêt (126, puis 318). Safari ne le fait pas : posez tabindex="0", role="region" et un aria-label (« Destinations », « Examens ») sur .sb-horiz-content. Si les vignettes deviennent des liens, retirez le tabindex : Tab fera défiler la vignette focalisée dans la vue.
  • Contraste. L'indice « Scrollez → » fait 10,4 px en blanc à 30 % sous une opacité qui pulse : de 1,22:1 à 1,83:1 sur #0a0a0f (2,62:1 sans la pulsation) — jamais les 4,5:1 requis, et le texte est codé en français. La racine impose ce fond sombre : le rapport ne dépend pas de la page hôte. Soit vous en faites une vraie consigne lisible, soit vous le passez en aria-hidden="true" et mettez la consigne dans l'aria-label du conteneur. Les chiffres blancs des vignettes : 4,47:1 sur #6366f1 mais 2,15:1 sur #f59e0b, 2,28:1 sur #22c55e, 2,43:1 sur #06b6d4 — des espaces réservés à remplacer ; si du texte reste posé sur une image, ajoutez un voile sombre.
  • Lecteurs d'écran et affordance. Six div sans sémantique de liste : passez en ul/li (ou role="list") pour que le nombre d'éléments soit annoncé ; les vignettes hors champ restent dans l'arbre d'accessibilité, ce qui est correct. overflow: auto laisse la barre de défilement native, non stylée : sur les systèmes à barres classiques (Windows, macOS en « toujours afficher ») elle occupe le bas des 280 px du conteneur — c'est l'unique affordance des utilisateurs à souris, ne la masquez pas (scrollbar-width: none) sans ajouter des boutons.

Compatibilité navigateur

Requiert scroll-snap-type / scroll-snap-align (spécification 2018), la syntaxe à deux valeurs overflow: auto hidden et gap en contexte flex — c'est cette dernière qui fixe le seuil. Aucun JavaScript, aucune variable CSS, aucune animation-timeline : contrairement aux effets pilotés au défilement en CSS natif de cette catégorie, celui-ci fonctionne dans Firefox.

Chrome 84+✓ Complet
Firefox 68+✓ Complet
Safari 14.1+✓ Complet
Edge 84+✓ Complet
Mobile iOS 14.5+✓ Complet (glissement au doigt, arrêt par vignette)
Android Chrome 84+✓ Complet (glissement au doigt, arrêt par vignette)

Sans JavaScript : rien à perdre, il n'y en a pas. Safari 13.1 à 14.0 ignore le gap en flex : les vignettes se touchent, le défilement et le snap fonctionnent. Safari 11 à 13.0 rejette overflow: auto hidden : le conteneur n'en est plus un, la piste de 1 172 px déborde et rien ne défile — écrivez overflow-x: auto; overflow-y: hidden. Sans snap (Firefox avant 68) : défilement horizontal libre, sans arrêt sur les vignettes.

Le code

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

index.html — structure
<div class="sb-horiz">
  <div class="sb-horiz-content">
    <div class="sb-horiz-track">
      <div class="sb-horiz-item">1</div>
      <div class="sb-horiz-item">2</div>
      <div class="sb-horiz-item">3</div>
      <div class="sb-horiz-item">4</div>
      <div class="sb-horiz-item">5</div>
      <div class="sb-horiz-item">6</div>
    </div>
  </div>
  <span class="sdc-scroll-hint">Scrollez →</span>
</div>
🔒 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
Borne du conteneur (CSS — .sb-horiz-content) width: 100% ; max-width: 900px ; margin: 0 auto La règle : ça défile tant que la piste (1 172 px pour six vignettes) est plus longue que le conteneur. Mesuré à 1 280 px de page : 272 px de course avec la borne, zéro sans. Adaptez-la à votre colonne, ou retirez-la si la colonne est déjà plus étroite que la piste ; élargissez la piste (plus de vignettes, ou plus larges) avant d'élargir le conteneur.
Fond, couleur et police (CSS — .sb-horiz) #0a0a0f, #fff, sans-serif La racine impose son fond : c'est ce qui garde l'indice visible sur une page blanche. Pour un carousel sur fond clair, changez ici le fond et recolorez l'indice (rgba(28,27,25,.55) dans la scène du showroom) ; la police n'affecte que les chiffres et l'indice.
Taille des vignettes (CSS — .sb-horiz-item) 180 × 140 px Chaque vignette peut avoir sa propre largeur (classe ou style) : scroll-snap-align: center centre n'importe quelle largeur. Plus la vignette est étroite par rapport au conteneur, plus les voisines dépassent.
scroll-snap-align (CSS — .sb-horiz-item) center start aligne le bord gauche de la vignette sur celui du conteneur : la première devient atteignable à 0 et la dernière se cale à droite. Ajoutez alors scroll-padding-inline-start: 16px au conteneur pour respecter le retrait de la piste.
scroll-snap-type (CSS — .sb-horiz-content) x mandatory x proximity ne pose le défilement sur une vignette que lorsqu'on s'arrête près d'elle, et autorise les positions intermédiaires ; none rend le défilement libre. Avec mandatory, même un scrollLeft écrit en JavaScript est rabattu sur un arrêt (100 → 126 mesuré).
Espacement et retrait de la piste (CSS — .sb-horiz-track) gap: 12px ; padding: 20px 16px Le gap est la marge visible entre une vignette centrée et ses voisines ; le padding fixe la position des butées. Pour que la première et la dernière puissent se centrer, montez le padding horizontal à la moitié du conteneur moins la moitié d'une vignette (274 px à 728 px de large).
Indice « Scrollez → » (HTML/CSS — .sdc-scroll-hint) .65rem, rgba(255,255,255,.3), pulsation 2 s Ancré sur la racine .sb-horiz (position: relative) : laissez-le hors du conteneur défilant, sinon il part avec le contenu. Contraste 1,22 à 1,83:1 : à remplacer par une consigne lisible, ou à supprimer avec sa règle et l'animation sdcHintPulse.
Fonds des vignettes (HTML — attributs style) 6 dégradés à 135° chaînés (#6366f1 → … → #06b6d4) Remplacez par background: url(…) center/cover ou une img en object-fit: cover dans la vignette ; les dégradés sont en ligne, ils disparaissent avec le HTML.

FAQ

Parce qu'un conteneur ne défile que si son contenu dépasse. La piste fait 1 172 px pour six vignettes : sur une page de 1 280 px, un conteneur en width: 100% sans borne fait 1 264 px, les six vignettes tiennent sur une ligne et scrollWidth = clientWidth — course nulle, mesuré. À 900 px, la course est de 272 px et le snap retrouve ses arrêts (0, 40, 232, 272). Vous pouvez monter la borne, ou la retirer, tant que votre piste reste plus longue que votre colonne : avec vos propres vignettes (dix photos de 240 px, par exemple), la piste dépasse 2 500 px et la borne devient inutile. L'aperçu du catalogue fait 744 px : il défile avec ou sans.
Même conteneur (overflow: auto hidden, scroll-snap-type: x mandatory) pour les deux premiers, mais pas le même contenu. Horizontal Snap : des diapositives en min-width: 100% alignées start, une seule visible à la fois, aucun aperçu des voisines. Card Snap Carousel : des cartes de 200 × 240 px avec une structure imposée (image de 100 px, titre, texte, étiquette), gap et padding sur le conteneur lui-même, et scroll-behavior: smooth. Sticky Horizontal ne défile pas du tout : sa piste glisse sur une animation CSS de 12 s (translateX de 0 à −400 %) dans un overflow: hidden. Horizontal Scroll est la version nue : 1,4 Ko de CSS, vignettes de 180 × 140 px sans structure interne, largeur libre par vignette, pas de scroll-behavior — à choisir quand l'utilisateur doit garder la main sur la vitesse et voir ce qui vient.
Mesuré : trois crans de molette verticale laissent scrollLeft à 0 — un conteneur horizontal n'écoute que l'axe horizontal (Maj + molette, molette latérale, pavé tactile, barre de défilement, ou Tab puis Flèche droite dans Chrome 130+ et Firefox). Pour des boutons, un scrollBy({ left: 192, behavior: 'smooth' }) suffit (largeur + gap) : avec mandatory, Chromium rabat la position sur l'arrêt le plus proche, donc même un décalage approximatif tombe sur une vignette (100 → 126 mesuré). Pour désactiver la flèche en butée, comparez scrollLeft à scrollWidth − clientWidth dans un écouteur scroll, ou scrollend si vous n'avez pas besoin de Safari avant la 26.