Sticky Horizontal
Cinq panneaux de la largeur du cadre sur une piste flex, poussée par un seul @keyframes de 12 s en boucle infinie : 2,16 s d'arrêt par panneau, 240 ms de glissement linéaire, puis 600 ms de rembobinage qui retraversent les quatre panneaux. Ni scroll, ni JavaScript, ni écouteur : mesuré, molette, survol et clic n'y changent rien — le bandeau tourne même sur un écran que personne ne touche.
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
Anatomie : .sticky-h-viewport (width: 100%, height: 280px, overflow: hidden) contient .sticky-h-track, une boîte flex de la hauteur du cadre, qui aligne cinq .sticky-h-panel en min-width: 100% : chaque panneau mesure la largeur de la piste — donc celle du cadre — et les cinq débordent quatre fois à droite (mesuré sur une page vierge de 800 px : cadre 784 px, piste 784 px, scrollWidth 3 920 px). Chaque panneau est une colonne flex centrée (gap: 12px, padding: 24px) qui porte un chiffre .panel-number — 2.5rem soit 40 px, font-weight: 800, dégradé #a855f7 → #ec4899 découpé dans les glyphes par background: … text et -webkit-text-fill-color: transparent — et un libellé .panel-label (.85rem, 13,6 px, blanc à 50 %). Cinq fonds par nth-child, un dégradé à 135° chacun : #0f0f23 → #1a1a3e, #1a0f23 → #2d1a3e, #0f1a23 → #1a2d3e, #1a230f → #2d3e1a, #230f1a → #3e1a2d.
L'horloge : animation: 12s linear 0s infinite normal none running slideHorizontal sur la piste. Les images-clés posent translateX(0) de 0 à 18 %, -100% de 20 à 38 %, -200% de 40 à 58 %, -300% de 60 à 78 %, -400% de 80 à 95 %, puis 0 à 100 %. Les pourcentages de translateX se rapportent à la largeur de la piste elle-même, c'est-à-dire à un panneau : chaque palier décale d'un panneau exactement. Chronologie mesurée, identique dans Chromium, Firefox et WebKit : panneau 1 de 0 à 2,16 s, glissement de 2,16 à 2,40 s (784 px en 240 ms, 3,3 px/ms, linear donc sans amorti), panneau 2 de 2,40 à 4,56 s, panneau 3 de 4,80 à 6,96 s, panneau 4 de 7,20 à 9,36 s, panneau 5 de 9,60 à 11,40 s — 1,8 s seulement, son palier ne fait que 15 %. De 95 à 100 %, la piste revient de -400% à 0 en 600 ms : un rembobinage qui traverse les panneaux 4, 3 et 2 à l'envers à 5,2 px/ms (mesuré : -300% à 11,55 s, -200% à 11,7 s, -100% à 11,85 s). Le cycle repart sur le panneau 1 à 12,0 s.
Ce que le code n'est pas : il n'y a ni position: sticky, ni conteneur défilable, ni JavaScript (la clé js est vide), ni id, ni variable CSS, ni écouteur. Mesuré : 1 000 ms d'horloge murale font 1 000 ms d'animation ; cinq crans de molette de 120 px à la verticale puis à l'horizontale sur le cadre laissent scrollLeft et scrollTop à 0 et l'animation running ; le survol la laisse running ; le clic aussi. Seule transform change : le mouvement reste sur le compositeur, aucune mise en page par image. La conséquence est double : la chronologie ne dépend de rien (écran d'accueil, borne, mur d'atelier), et elle n'obéit à rien — rien ne la met en pause, rien ne ramène un lecteur au panneau précédent. Trois restes du catalogue : le prélude .demo-preview (min-height: 300px, centrage flex) laisse deux bandes de 10 px de #0a0a0f au-dessus et au-dessous du cadre de 280 px ; les libellés « Decouverte », « Creation », « Iteration » sont écrits sans accent ; le nom slideHorizontal est générique — un autre @keyframes du même nom déclaré plus bas dans la page remplacerait celui-ci.
Accessibilité
- prefers-reduced-motion absent du code : mesuré avec la préférence activée, l'animation est
runninget la chronologie identique (2,16 s, 240 ms, rembobinage de 600 ms). Ajoutez@media (prefers-reduced-motion: reduce) { .sticky-h-track { animation: none } }— mais seul, ce repli ne laisse que le premier panneau visible : complétez par.sticky-h-viewport { overflow-x: auto; scroll-snap-type: x mandatory }et.sticky-h-panel { scroll-snap-align: start }, l'horloge s'arrête et les cinq panneaux restent atteignables au doigt ou à la molette. - Ni pause ni arrêt (WCAG 2.2.2 : un contenu qui bouge seul plus de 5 s doit pouvoir être mis en pause) : aucun bouton, aucun survol — mesuré,
animation-play-stateresterunningsous le pointeur. Le minimum tient en une règle :.sticky-h-viewport:hover .sticky-h-track, .sticky-h-viewport:focus-within .sticky-h-track { animation-play-state: paused }; mieux, un bouton qui pose une classe.is-paused. Sur un écran sans utilisateur — kiosque, mur d'atelier — la règle n'a pas d'objet, et c'est précisément là que cet effet est à sa place. - Contraste : le libellé blanc à 50 % mesure de 4,17:1 (panneau 4, extrémité
#2d3e1a) à 5,31:1 sur les cinq fonds — conforme partout sauf sur le bord clair du panneau 4 (seuil 4,5:1). Le chiffre, 40 px en gras, est un grand texte (seuil 3:1) : 3,56 à 5,35:1 sur les panneaux 1, 2, 3 et 5, mais 2,92:1 pour l'extrémité rose#ec4899sur le panneau 4. Sur une page claire, si les panneaux perdent leurs fonds sombres, le libellé disparaît (blanc sur blanc) : remplacezrgba(255, 255, 255, .5)par une couleur pleine. - Lecteurs d'écran et clavier : les cinq panneaux restent dans le DOM et dans l'arbre d'accessibilité quelle que soit leur position — un lecteur lit les cinq libellés dans l'ordre, rien n'est annoncé au changement de panneau puisque rien ne change dans le DOM. Pour un bandeau décoratif, posez
aria-hidden="true"sur.sticky-h-viewport. Si vous mettez des liens dans les panneaux, le focus clavier peut atterrir sur un panneau translaté hors du cadre, invisible sousoverflow: hidden: mettez l'animation en pause sur:focus-withinet laissez le navigateur défiler (overflow-x: auto), ou gardez les panneaux inertes.
Compatibilité navigateur
CSS seul : @keyframes et transform: translateX(), flexbox, background-clip: text via le raccourci background: … text et -webkit-text-fill-color. Aucune ligne de JavaScript, aucune variable CSS, aucune ressource. C'est une animation temporelle classique : elle tourne dans Firefox, contrairement aux défilements horizontaux pilotés par animation-timeline de la même catégorie (Horizontal Scroll Section fx-0567, Horizontal Scroll fx-0564), que Firefox n'exécute dans aucune version.
Là où background-clip: text n'existe que sous préfixe (Chrome et Edge avant 120, Safari avant 15.5, Firefox avant 49), la déclaration background: … text est rejetée en bloc et -webkit-text-fill-color: transparent reste : les chiffres sont invisibles, les libellés et le glissement fonctionnent. Repli : .panel-number { color: #c084fc } en base, et le dégradé, -webkit-background-clip: text, background-clip: text et le -webkit-text-fill-color ensemble dans @supports ((background-clip: text) or (-webkit-background-clip: text)) { … }. Sans @keyframes — aucun navigateur courant — les cinq panneaux restent figés sur le premier.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="sticky-h-viewport">
<div class="sticky-h-track">
<div class="sticky-h-panel">
<span class="panel-number">01</span>
<span class="panel-label">Decouverte</span>
</div>
<div class="sticky-h-panel">
<span class="panel-number">02</span>
<span class="panel-label">Exploration</span>
</div>
<div class="sticky-h-panel">
<span class="panel-number">03</span>
<span class="panel-label">Creation</span>
</div>
<div class="sticky-h-panel">
<span class="panel-number">04</span>
<span class="panel-label">Iteration</span>
</div>
<div class="sticky-h-panel">
<span class="panel-number">05</span>
<span class="panel-label">Livraison</span>
</div>
</div>
</div>
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 |
|---|---|---|
Durée du cycle (CSS — .sticky-h-track { animation: 12s … }) |
12 s : 2,16 s par panneau, 240 ms de glissement, 600 ms de rembobinage | Tout est proportionnel : à 20s, 3,6 s d'arrêt, 400 ms de glissement, 1 s de rembobinage. Sous 8 s le glissement (160 ms) devient une coupure ; au-delà de 30 s, le rembobinage dépasse 1,5 s et se voit vraiment. |
Arrêt et glissement (CSS — @keyframes slideHorizontal : 0%, 18% … 20%, 38%) |
18 % d'arrêt et 2 % de glissement par panneau, 15 % sur le dernier, 5 % de retour | Écartez les deux bornes (17% / 21%) pour un glissement deux fois plus long. Pour un départ et une arrivée en douceur, ajoutez animation-timing-function: ease-in-out dans le bloc de chaque palier : la fonction posée sur une image-clé s'applique jusqu'à la suivante, sans toucher au linear global. |
Nombre de panneaux (HTML — 5 × .sticky-h-panel ; keyframes jusqu'à translateX(-400%)) |
5, couplés aux 5 paliers | Les paliers sont écrits en dur : un 6ᵉ panneau n'est jamais montré, avec 4 panneaux le palier -400% affiche 1,8 s de vide. Pour 6 panneaux : 0%, 14% → 0, 16%, 30% → -100%, 32%, 46% → -200%, 48%, 62% → -300%, 64%, 78% → -400%, 80%, 95% → -500%, to → 0. |
Hauteur (CSS — .sticky-h-viewport { height: 280px }) |
280 px | Les panneaux suivent (height: 100%) ; la largeur est toujours celle du conteneur. 112 px suffisent à un bandeau de logos. Le prélude .demo-preview ajoute 10 px de fond au-dessus et au-dessous (min-height: 300px) : retirez-le à l'intégration. |
Fonds des panneaux (CSS — .sticky-h-panel:nth-child(1…5) { background: linear-gradient(135deg, …) }) |
5 dégradés sombres à 135°, du bleu nuit au bordeaux | Un dégradé par position, pas par contenu : réordonnez les panneaux, les fonds ne suivent pas. Une teinte différente par panneau rend le glissement lisible même quand les textes se ressemblent. |
Chiffre (CSS — .panel-number { font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #a855f7, #ec4899) text }) |
40 px, graisse 800, violet → rose | Le dégradé est découpé dans les glyphes ; il change avec les deux couleurs. Un texte court — certification, prix, nom de poste — tient sur une ligne jusqu'à 10 caractères à 40 px dans un cadre de 540 px. Voir la compatibilité pour le repli sans background-clip: text. |
Libellé (CSS — .panel-label { font-size: .85rem; color: rgba(255, 255, 255, .5) }) |
13,6 px, blanc à 50 % | Contraste de 4,17 à 5,31:1 sur les fonds vendus ; passez à .75 et 1rem pour un écran lu à distance. Le texte n'est pas centré quand il passe sur deux lignes : ajoutez text-align: center. |
| Pause et arrêt (CSS — à ajouter) | aucun : survol, clic et molette mesurés sans effet | .sticky-h-viewport:hover .sticky-h-track { animation-play-state: paused } suffit pour une page web ; sous prefers-reduced-motion: reduce, animation: none sur la piste et overflow-x: auto; scroll-snap-type: x mandatory sur le cadre pour que les cinq panneaux restent atteignables. |
FAQ
95% (translateX(-400%)) à 100% (translateX(0)), la piste revient au départ en 5 % du cycle, soit 600 ms pour quatre largeurs — mesuré 5,2 px/ms sur un cadre de 784 px, les panneaux 4, 3 et 2 passant à l'envers (-300% à 11,55 s, -200% à 11,7 s, -100% à 11,85 s). Le code ne bégaie pas, il rembobine. Pour une boucle sans retour visible : ajoutez un 6ᵉ panneau, copie du premier, et remplacez la fin des images-clés par 80%, 98% { transform: translateX(-400%) } puis to { transform: translateX(-500%) }. À 100 % la copie occupe le cadre, à 0 % l'original la remplace au pixel près : le saut est invisible. Le sixième fond nth-child(6) doit reprendre le premier.overflow: hidden, une piste flex, des panneaux, un @keyframes en translateX — mais deux horloges. fx-0567 branche l'animation sur le défilement (animation-timeline: --hscrollTimeline, conteneur haut de 400 %) : l'utilisateur fait avancer la piste en scrollant, rien ne bouge s'il ne scrolle pas, et Firefox n'exécute animation-timeline dans aucune version. fx-0578 branche la même piste sur le temps (12s linear infinite) : elle avance sans personne, ne s'arrête pour personne et tourne dans Firefox comme partout — mesuré ici, molette, survol et clic laissent la chronologie intacte. Choisissez fx-0567, ou Horizontal Scroll fx-0541 en scroll-snap natif, quand un lecteur tient la page ; fx-0578 quand l'écran n'a pas de lecteur — hall d'accueil, borne, atelier — ou pour un bandeau secondaire qu'on regarde sans le piloter.position: sticky ; ce code en reproduit l'image, un cadre fixe et des panneaux qui défilent, avec une simple animation temporelle : aucun position: sticky, aucun conteneur défilable, aucun JavaScript. La description du catalogue le dit à sa manière (« défilement horizontal automatique simulant une navigation par panneaux »). Conséquence pratique : posez-le dans n'importe quel bloc de la page, il n'a besoin ni d'une hauteur de scroll ni d'un parent particulier — mais ne comptez pas sur lui pour réagir au scroll.