Horizontal Scroll
Six tuiles de 200 × 200 px — numéro à 32 px et libellé court — dans une piste flex à 12 px d'écart que scroll-snap-type: x mandatory arrête tuile centrée, un pas de 212 px, en CSS seul (zéro octet de JavaScript). Mesuré sur 524 px de large : un cran de molette horizontale ou un swipe de 150 px avance d'une tuile ; la première et la dernière ne se centrent jamais (±146 px) ; au-delà de 1 292 px de conteneur, plus rien ne défile alors que l'indice « Scrollez → » pulse toujours.
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
Trois niveaux et pas une ligne de script. .ss-horiz-content est la fenêtre : height: 280px, overflow: auto hidden (défilement horizontal, vertical coupé), display: flex avec align-items: center, et surtout scroll-snap-type: x mandatory. Dedans, .ss-horiz-track aligne en flex six .ss-horiz-item avec gap: 12px et padding: 20px 16px. Chaque tuile : flex-shrink: 0, 200 × 200 px, border-radius: 14px, colonne centrée, libellé en .9rem (14,4 px) gras, un span pour le numéro en 2rem (32 px), graisse 800, opacity: .5, et scroll-snap-align: center. Six dégradés à 135° par :nth-child (indigo → violet → fuchsia → rose → ambre → vert → cyan). La piste mesure 6 × 200 + 5 × 12 + 32 = 1 292 px. Aucune variable CSS, js vide : le défilement est celui du navigateur, sur le compositeur, sans écouteur.
Mesuré dans Chromium sur une fenêtre de 524 px : scrollWidth 1 292, course maximale 768 px. Les positions d'arrêt sont les centres des tuiles moins la demi-fenêtre : 66, 278, 490, 702 — un pas de 212 px (200 + 12). La tuile 1 exigerait −146 px et la tuile 6 +914 : la butée les rabat à 0 et 768, donc ni la première ni la dernière ne se centrent, elles restent décalées de 146 px. mandatory interdit toute position intermédiaire : un scrollLeft forcé à 50 se pose à 66, à 320 → 278, à 900 → 768. Un cran de molette horizontale (deltaX 120) avance d'une tuile exactement (66 → 278 → 490 → 702 → 768) ; la molette verticale ne fait rien (0 px) ; sans JavaScript, tirer à la souris ne fait rien non plus — au trackpad, au tactile ou par la barre de défilement seulement. Sur iPhone 13 émulé (374 px), un swipe de 150 px donne 141, 353, 565, 777 (même pas de 212), un swipe de 40 px revient à 0. Pas de scroll-behavior: smooth : une écriture de scrollLeft saute ; scrollTo({ behavior: 'smooth' }) glisse (0 → 490 px en ~320 ms) et le snap termine.
Ce que le code ne règle pas. La fenêtre n'a aucune largeur propre : enfant flex du .demo-preview vendu (centré), elle rétrécit à la largeur du parent parce qu'un conteneur défilant a min-width: 0 ; dans un parent bloc elle prend 100 %. Dès que le parent dépasse 1 292 px, la piste tient entière, centrée, et rien ne défile plus (mesuré à 1 400 px : course 0) ; à 1 264 px il reste un moignon de 28 px entre deux positions de snap. Dans les deux cas .sdc-scroll-hint — « Scrollez → », préfixe sdc- partagé avec les autres démos de défilement du catalogue, pulsation d'opacité .3 ↔ .7 en 2 s — continue de clignoter. Rien ne marque la tuile centrée (pas d'état actif), et le HTML vendu mêle des libellés anglais (Design, Develop, Test…) à un indice en français. Le tag « Sticky » du catalogue ne correspond à rien dans le code : la variante collante et auto-animée est Sticky Horizontal (fx-0578).
Accessibilité
- prefers-reduced-motion absent du code — et, pour une fois, presque sans conséquence : le défilement n'est déclenché que par l'utilisateur (aucune animation liée au scroll, aucun mouvement automatique). Seule la pulsation de l'indice l'ignore : mesuré sous
reducedMotion: reduce, son opacité oscille toujours de 0,34 à 0,70 en une seconde. Ajoutez@media (prefers-reduced-motion: reduce) { .sdc-scroll-hint { animation: none; opacity: .5 } }, ou retirez l'indice à l'intégration. - Clavier :
.ss-horiz-contentn'a pas detabindex. Chrome 130+ rend focalisable tout conteneur défilant sans enfant focalisable — mesuré : Tab s'arrête dessus et → avance d'une tuile (scrollLeft 0 → 66) —, Firefox et Safari non. Poseztabindex="0"et unaria-labelsur la fenêtre. Si les tuiles servent de sélecteur (familles, chapitres), ce sont desdiv: remplacez-les par desbuttonou des liens et gérezaria-currentsur celle qui commande le contenu. - Contraste insuffisant sur cinq tuiles sur six (libellé blanc 14,4 px gras, seuil 4,5:1) : tuile 1 indigo 4,4:1, tuiles 2 et 3 entre 3,5 et 3,9:1, tuiles 4 à 6 (rose → ambre → vert → cyan) entre 2,2 et 2,9:1. Le numéro à
opacity: .5tombe entre 1,5 et 2,2:1. Corrigez par des arrêts de dégradé plus sombres (#b45309,#15803d,#0e7490à la place de l'ambre, du vert et du cyan), untext-shadow: 0 1px 2px rgba(0,0,0,.35), ou un libellé d'au moins 18,7 px gras (texte large, seuil 3:1). - Lecteurs d'écran et intégration : six
divlus « 01 Design », « 02 Develop »… dans l'ordre, sans rôle ni annonce de la tuile centrée — la position de défilement n'a aucune sémantique. Le tactile fonctionne nativement (une tuile par swipe, un swipe court revient en place). Pour que la première et la dernière tuile se centrent comme les autres, donnez à la pistepadding: 20px calc(50% - 100px)(c'est ce que font les trois scènes) ; pour ne pas tromper l'utilisateur sur grand écran, limitez la fenêtre (max-width) ou masquez l'indice quandscrollWidthne dépasse pasclientWidth.
Compatibilité navigateur
Feuille de style seule, sans JavaScript : scroll-snap-type: x mandatory et scroll-snap-align: center (syntaxe de 2018), overflow à deux valeurs (auto hidden), gap en flexbox, dégradés linéaires. Le défilement est natif : l'effet fonctionne dans Firefox, contrairement aux animations pilotées au scroll en CSS (animation-timeline), que Firefox n'active dans aucune version.
Sans scroll-snap (navigateurs plus anciens), la piste défile librement et s'arrête n'importe où, mais tout reste visible et lisible. Sans gap en flexbox (Safari 13 et avant), les tuiles se touchent et le pas passe de 212 à 200 px. Rien ne dépend d'un script : il n'y en a pas.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="ss-horiz-content">
<div class="ss-horiz-track">
<div class="ss-horiz-item">
<span>01</span> Design
</div>
<div class="ss-horiz-item">
<span>02</span> Develop
</div>
<div class="ss-horiz-item">
<span>03</span> Test
</div>
<div class="ss-horiz-item">
<span>04</span> Deploy
</div>
<div class="ss-horiz-item">
<span>05</span> Monitor
</div>
<div class="ss-horiz-item">
<span>06</span> Iterate
</div>
</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 |
|---|---|---|
Taille des tuiles (CSS — .ss-horiz-item width / height) |
200 × 200 px | Le pas de snap vaut largeur + gap, soit 212 px : c'est la distance d'un cran de molette ou d'un swipe. Le nombre de tuiles visibles = largeur de la fenêtre ÷ 212 (deux et demie à 524 px). Les scènes descendent à 128 × 92 px sans rien changer d'autre. |
Écart (CSS — .ss-horiz-track gap) |
12px | S'ajoute au pas de snap. À 0, les tuiles se touchent et le pas passe à 200 px ; à 24, les dégradés respirent mais une tuile de moins tient à l'écran. |
Gouttières de la piste (CSS — .ss-horiz-track padding) |
20px 16px | Avec 16 px, la première et la dernière tuile s'arrêtent à 146 px du centre. padding: 20px calc(50% - 100px) (moitié de la fenêtre moins une demi-tuile) les centre comme les autres — c'est le réglage des trois scènes. |
Rigueur de l'arrêt (CSS — .ss-horiz-content scroll-snap-type) |
x mandatory | x proximity ne recale que près d'une tuile et laisse s'arrêter entre deux — préférable si les tuiles dépassent la fenêtre (mandatory rendrait l'excédent inatteignable). none : défilement libre, l'effet disparaît. |
Point d'alignement (CSS — .ss-horiz-item scroll-snap-align) |
center | start aligne la tuile sur le bord gauche : la première se pose alors naturellement à 0 et le padding gauche reste visible via scroll-padding-left: 16px sur la fenêtre. |
Nombre de tuiles et couleurs (HTML + CSS — :nth-child(1) à (6)) |
6 dégradés à 135° | Une septième tuile n'a aucun fond : ajoutez une règle :nth-child(7) ou passez les dégradés en style inline comme le fait fx-0541. La piste s'allonge de 212 px par tuile. |
Hauteur de la fenêtre (CSS — .ss-horiz-content height) |
280px | La piste (200 + 2 × 20 = 240 px) y est centrée. Réduisez-la avec les tuiles : 118 px pour des tuiles de 92 px dans la scène catalogue. |
Défilement programmé (CSS — scroll-behavior, absent) |
auto (saut instantané) | Pour qu'un bouton « suivant » ou un scrollLeft = … glisse, ajoutez scroll-behavior: smooth sur la fenêtre, ou appelez scrollTo({ left, behavior: 'smooth' }) — mesuré : 0 → 490 px en ~320 ms, le snap finit le geste. |
FAQ
scroll-snap-align: center demande une position de défilement négative pour la première (centre à 116 px, demi-fenêtre 262 px : −146) et au-delà de la course pour la dernière (+914 px pour 768 de course). Le navigateur rabat ces positions aux butées 0 et 768 : les deux tuiles s'arrêtent à 146 px du centre, mesuré sur 524 px. Deux corrections : des gouttières de piste égales à la demi-fenêtre moins une demi-tuile (padding: 20px calc(50% - 100px)), ou l'alignement start avec scroll-padding-left. Sans cela, un script « tuile active = la plus proche du centre » ne sélectionnera jamais la première ni la dernière.max-width sur .ss-horiz-content (720 px = trois tuiles centrées), ou masquez l'indice quand scrollWidth ≤ clientWidth — une ligne de JavaScript, la seule que l'effet demanderait.style inline : des vignettes, pas des libellés. fx-0586 ajoute un libellé court sous un numéro à moitié transparent dans une tuile carrée de 200 px : c'est la version « étapes ou familles nommées ». Les autres voisins du lot répondent à d'autres besoins — Horizontal Snap (fx-0594) : panneaux plein écran un par un ; Card Snap Carousel (fx-0595) : cartes 200 × 240 px avec zone image, texte et étiquette, plus scroll-behavior: smooth ; Sticky Horizontal (fx-0578) : défilement automatique en 12 s, sans contrôle de l'utilisateur.