Scroll✨ Premium

Card Snap Carousel

Six cartes de 200 × 240 px dans une piste de 280 px en scroll-snap-type: x mandatory, chacune calée au centre (scroll-snap-align: center) : les voisines dépassent des deux bords tant que la piste fait moins de 624 px. Aucun JavaScript — et scroll-behavior: smooth ne lisse que les défilements programmés, pas la molette.

CSSCarouselSnap

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

Comparateur de formules d'abonnement SaaS — cinq plans dans une piste de 540 px — Card Snap Carousel exemple 1

① Comparateur de formules d'abonnement SaaS — cinq plans dans une piste de 540 px

QuandPage tarifs d'un outil de gestion de projet : Free, Starter, Pro, Team, Enterprise, chacun sur une carte de 200 px avec son prix, son quota et une mention clé.
PourquoiÀ 540 px de piste, la carte centrée laisse 158 px de chaque voisine : le prospect lit un plan sans perdre de vue le précédent et le suivant, et compare de proche en proche. fx-0594 (Horizontal Snap, sections à min-width: 100%) masque toutes les autres formules pendant la lecture d'une seule — la comparaison côte à côte, celle qui aide à décider, disparaît.
RéglagesCinq cartes ; le prix en 1,5 rem dans .sn-carousel-img à la place du numéro, dégradés recolorés en indigo ; le plan recommandé reçoit une bordure d'accent ; ouverture sur la carte 3 par une écriture de scrollLeft, que scroll-behavior lisse.
Programme d'un festival de musique indépendant — une scène et un horaire par carte — Card Snap Carousel exemple 2

② Programme d'un festival de musique indépendant — une scène et un horaire par carte

QuandPage ou application du festival : Scène A, Scène B, Outdoor, Club et l'after ; sur chaque carte le créneau, l'artiste et le genre.
PourquoiLes bords visibles disent « il y a d'autres scènes » sans rien imposer : le festivalier s'arrête sur une carte, la lit, puis continue. fx-0578 (Sticky Horizontal) enchaîne ses cinq panneaux tout seul, 2,2 s chacun sur un cycle de 12 s — pas le temps de lire un horaire avant le suivant.
RéglagesCinq cartes ; .sn-carousel-img réduit au créneau horaire au-dessus du nom de la scène ; dégradés chauds ; piste laissée à 280 px sous un en-tête de 80 px ; indice « Scrollez → » conservé.
Catalogue de cas clients d'un cabinet de conseil — six références en défilement horizontal — Card Snap Carousel exemple 3

③ Catalogue de cas clients d'un cabinet de conseil — six références en défilement horizontal

QuandPage « Références » d'un cabinet : monogramme du client, résultat chiffré, secteur, un tag par domaine d'intervention.
PourquoiLe dépassement des cartes voisines communique l'abondance du catalogue mieux qu'une liste. fx-0586 (Horizontal Scroll) cale aussi ses tuiles au centre, mais chacune n'est qu'un numéro et un mot dans un carré de 200 px : rien à lire, rien à comparer d'une tuile à l'autre. Ici chaque carte porte un résultat.
RéglagesSix cartes, les six dégradés nth-child d'origine, le monogramme à la place du numéro, le résultat clé en h4, le secteur en p, le tag = domaine.

Comment ça marche

.sn-carousel-container est la piste : width: 100%, height: 280px, overflow: auto hidden (défile en x, jamais en y), scroll-snap-type: x mandatory, scroll-behavior: smooth, flex avec align-items: center, gap: 12px et padding: 0 12px. Dedans, six .sn-carousel-card de 200 × 240 px (flex-shrink: 0, border-radius: 14px, fond blanc à 4 %, bordure de 1 px à 8 %), chacune portant scroll-snap-align: center. Une carte = un bandeau .sn-carousel-img de 100 px (numéro 01 à 06 en 1,5 rem / 800, dégradé à 135° propre à chaque nth-child : indigo → violet → fuchsia → rose → ambre → vert → cyan) et un corps .sn-carousel-body (padding 12 px, colonne flex, gap 4 px : h4 .82 rem, p .68 rem à 50 % de blanc, .sn-carousel-tag .65 rem violet poussé en bas par margin-top: auto). Piste totale : 12 + 6 × 200 + 5 × 12 + 12 = 1 284 px. Le HTML vendu se termine par .sdc-scroll-hint « Scrollez → », absolu à 8 px du bas de .demo-preview, dont l'opacité pulse de .3 à .7 toutes les 2 s (sdcHintPulse) — un fragment du catalogue partagé par 44 effets de la catégorie, pas une pièce du carousel. Zéro JavaScript.

L'arithmétique du snap : le centre de la carte n est à 112 + 212 × (n − 1) px du début de la piste ; la position d'arrêt vaut ce centre moins la moitié de la largeur visible, bornée entre 0 et 1 284 − largeur. Mesuré dans Chromium : piste de 524 px → six arrêts (0, 62, 274, 486, 698, 760), les cartes 2 à 5 centrées avec 150 px de chaque voisine visible, les cartes 1 et 6 collées au bord ; piste de 384 px (mobile) → 80 px de voisine ; piste de 728 px → 40 px seulement ; piste de 784 px → quatre arrêts pour six cartes (0, 144, 356, 500) : les cartes 1-2 partagent la position 0, les cartes 5-6 la fin de course, aucune des quatre n'est jamais centrée. Le « peek » symétrique n'existe que sous 624 px de piste — au-delà, (largeur − 200) / 2 − 12 dépasse 200 px et la voisine est entière.

scroll-behavior: smooth n'agit que sur les défilements programmés : scrollLeft = 274 mesuré 0 → 3 → 14 → 42 → 105 → 180 → 207 → 226 → 239 → 254 → 260 → 265 à 16 ms d'intervalle, soit ≈ 200 ms ; en auto, la même écriture est instantanée (274 au premier relevé). À la molette, les relevés sont identiques avec ou sans la propriété : un cran horizontal de 120 px porte la piste à 119 puis le snap la ramène à 62 en ≈ 180 ms — c'est le lissage du navigateur, pas le CSS. La molette verticale ne fait rien (trois crans, 0 px) : sans trackpad, c'est Maj + molette ou la barre. Un coup de 600 px saute de 0 à 500, la fin de course, sans s'arrêter aux cartes intermédiaires : pas de scroll-snap-stop: always. Au clavier dans Chromium, Tab focalise la piste et Flèche droite avance d'un arrêt (0 → 144). Aucune erreur console.

Accessibilité

  • prefers-reduced-motion absent du code : mesuré à l'identique sous la préférence — le défilement programmé passe toujours par onze valeurs intermédiaires (1, 13, 41, 102…) et la pulsation sdcHintPulse reste en running. Ajoutez @media (prefers-reduced-motion: reduce) { .sn-carousel-container { scroll-behavior: auto } .sdc-scroll-hint { animation: none } }. Le snap lui-même n'est pas une animation : il reste, et c'est souhaitable.
  • Clavier : la piste n'a pas de tabindex. Chromium la rend focalisable d'office (mesuré : Tab → .sn-carousel-container, Flèche droite = un arrêt), Firefox aussi, Safari non. Posez tabindex="0", role="region" et un aria-label, puis un style :focus-visible — le CSS n'en contient aucun. Les cartes ne sont pas interactives ; si vous y placez des liens, chaque carte hors champ reste tabulable et le navigateur la ramène dans la vue, snap compris.
  • Contrastes sur le fond de carte (#141419 une fois le 4 % de blanc posé sur #0a0a0f) : titre blanc 18,4:1, p à 50 % de blanc 5,3:1, tag #a78bfa 5,8:1 — mais en 10,9 px et 10,4 px, trop petit pour un texte utile : passez à 12-13 px. Numéros blancs sur les dégradés : 4,5:1 (carte 1) à 2,2:1 (carte 5, ambre → vert) et 2,3-2,4:1 (carte 6), sous le seuil de 3:1 même pour du texte de 24 px. L'indice « Scrollez → » oscille de 2,6:1 à 9,7:1 en 10,4 px, et il est écrit en français dans le HTML.
  • Souris sans trackpad : molette verticale = 0 px (mesuré). Le code n'a ni bouton précédent/suivant ni indicateur de position : ajoutez deux boutons qui font scrollBy({ left: ±212 }) — la propriété scroll-behavior lissera le déplacement. Rien ne dit au lecteur d'écran qu'il y a six cartes : un <ul> ou role="list" / role="listitem". La barre de défilement n'est pas masquée (aucune règle scrollbar-width ni ::-webkit-scrollbar) : sous Windows elle prend ≈ 17 px dans les 280 px de la piste, sous les cartes. C'est le seul repère de position du composant — gardez-la ou remplacez-la par des points.

Compatibilité navigateur

CSS seul : scroll-snap-type: x mandatory / scroll-snap-align: center (syntaxe de 2018), overflow à deux valeurs, scroll-behavior: smooth, gap en flex et width: fit-content. Aucun animation-timeline : c'est du défilement natif, il fonctionne dans Firefox — contrairement aux animations pilotées au scroll en CSS, que Firefox ne rend dans aucune version. Zéro dépendance, zéro script.

Chrome 84+✓ Complet
Firefox 94+✓ Complet
Safari 15.4+✓ Complet
Edge 84+✓ Complet
Mobile iOS 15.4+✓ Complet (glissement au doigt)
Android Chrome✓ Complet (glissement au doigt)

Chrome 69-83 : snap et lissage complets mais gap ignoré en flex, les cartes se touchent. Firefox 68-93 : tout fonctionne sauf width: fit-content, la pastille de tag s'étire sur la largeur du corps. Safari 11-15.3 : snap natif, mais les défilements programmés sont instantanés (scroll-behavior ignoré) ; molette et doigt ne changent pas. Sans scroll-snap du tout : une rangée à défilement horizontal libre, sans arrêt sur les cartes.

Le code

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

index.html — structure
<div class="sn-carousel-container">
  <div class="sn-carousel-card">
    <div class="sn-carousel-img">01</div>
    <div class="sn-carousel-body">
      <h4>Titre</h4>
      <p>Description</p>
      <span class="sn-carousel-tag">Tag</span>
    </div>
  </div>
  <!-- × 6 cartes -->
</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
Largeur de carte (CSS — .sn-carousel-card width) 200px Avec gap: 12px, le pas entre deux arrêts vaut largeur + 12, et la part visible de chaque voisine vaut (piste − largeur) / 2 − 12. À 260 px sur une piste de 540 px : 128 px de voisine au lieu de 158.
scroll-snap-align (CSS — .sn-carousel-card) center start cale la carte active au bord gauche — ajoutez scroll-padding-left: 12px sur la piste pour respecter le padding — et rend la première carte atteignable, mais la dernière ne se cale plus.
scroll-snap-type (CSS — .sn-carousel-container) x mandatory x proximity ne cale que près d'un arrêt et laisse un défilement libre entre deux. Pour interdire de sauter des cartes (mesuré : un coup de 600 px va de 0 à la fin de course), ajoutez scroll-snap-stop: always sur .sn-carousel-card.
gap + padding (CSS — .sn-carousel-container) 12px / 0 12px Gardez-les égaux : la première et la dernière carte ont alors la même marge que l'espace entre deux cartes. Le padding fixe aussi la position de repos de la carte 1 (12 px du bord).
Hauteurs (CSS — .sn-carousel-container height / .sn-carousel-card height) 280px / 240px 20 px libres au-dessus et sous les cartes ; c'est là que se loge la barre de défilement quand le système l'affiche. À 260 px de piste, elle mord dans les cartes.
scroll-behavior (CSS — .sn-carousel-container) smooth Ne concerne que scrollLeft, scrollTo, scrollBy et les ancres (≈ 200 ms mesurés) ; la molette et le doigt n'en dépendent pas. À passer en auto sous prefers-reduced-motion.
Dégradés des bandeaux (CSS — .sn-carousel-card:nth-child(1…6) .sn-carousel-img) 6 dégradés à 135°, indigo → cyan Six règles pour six cartes : une septième carte n'a pas de fond. Écrivez nth-child(6n+1)… pour cycler, ou une seule couleur pour tous les bandeaux.
Indice « Scrollez → » (HTML — .sdc-scroll-hint) bottom: 8px, opacité .3 ↔ .7 en 2 s Positionné contre l'ancêtre positionné le plus proche (.demo-preview dans le code vendu). Traduisez-le, remplacez-le par des boutons ou supprimez-le : il est décoratif et son contraste tombe à 2,6:1 au creux de la pulsation.

FAQ

Parce que la position de repos est bornée par la zone défilable : centrer la carte 1 demanderait un scrollLeft négatif, centrer la carte 6 dépasserait la fin de course. Mesuré sur une piste de 784 px : les cartes 1 et 2 partagent l'arrêt 0, les cartes 5 et 6 l'arrêt 500 — quatre positions pour six cartes. Le correctif est un padding qui crée l'espace manquant : .sn-carousel-container { padding: 0 calc(50% - 100px) } (la moitié de la piste moins la moitié d'une carte). Chaque carte devient alors centrable, la première et la dernière comprises, et le nombre d'arrêts vaut le nombre de cartes quelle que soit la largeur.
Même préfixe sn-, même piste (overflow: auto hidden, x mandatory, smooth, 280 px), mais fx-0594 fait des sections à min-width: 100% calées en start : un panneau par arrêt, rien des voisins, toujours autant d'arrêts que de sections. fx-0595 fixe des cartes de 200 px calées au centre : les voisines dépassent, et le nombre d'arrêts dépend de la largeur (six à 524 px, quatre à 784 px). Prenez fx-0594 pour une séquence à lire un message à la fois, fx-0595 pour parcourir et comparer plusieurs éléments courts.
Une piste qui ne défile qu'en x n'écoute que les déplacements horizontaux : trackpad, geste tactile, Maj + molette ou la barre. Mesuré : trois crans verticaux, 0 px. Pour convertir la molette verticale : track.addEventListener('wheel', e => { if (!e.deltaY) return; e.preventDefault(); track.scrollBy({ left: e.deltaY }); }, { passive: false })scroll-behavior: smooth lisse le déplacement et le snap mandatory termine sur une carte. Le prix : la page ne défile plus verticalement tant que le pointeur est sur la piste, y compris en fin de course ; testez scrollLeft contre 0 et scrollWidth − clientWidth avant d'annuler l'événement. Deux boutons scrollBy({ left: ±212 }) restent la solution la plus lisible.