Basic Slider
Carrousel à flèches : la piste glisse d'une image à la suivante au clic, aux touches ← → ou au doigt, et repart de la première après la dernière.
Mis à jour le
Cet effet est gratuit — la catégorie Galerie en contient 44 au total, dont 6 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →
Exemples d'usage
CARNET DE LECTURE
Pourquoi les vieux romans nous semblent si lents
Les longues descriptions étaient le cinéma de leur époque : le lecteur n'avait jamais vu la mer, la ville ou la montagne, alors l'auteur lui montrait tout, ligne après ligne.
Aujourd'hui l'image arrive avant le texte, qui doit la rattraper.
« Décrire, c'était voyager à la place du lecteur. »
8 min de lecture · 3 commentaires
Comment ça marche
Les diapositives .bsld-slide sont alignées côte à côte dans .bsld-track, chacune large de 100 % du cadre .bsld-carousel. Changer de diapositive, c'est poser translateX(-100 %), -200 %… sur la piste ; la transition CSS de 0,5 s en ease-out fait le glissement. Après la cinquième, le bouton suivant ramène à la première : la piste revient en arrière en un seul mouvement, elle ne tourne pas en boucle infinie.
Trois façons de naviguer : les boutons ‹ et › (.bsld-prev, .bsld-next), les touches ← et → quand le focus est dans le carrousel (sur l'une des flèches, par exemple), et un glisser horizontal de plus de 50 px, au doigt comme à la souris. Le glisser passe par les Pointer Events ; un mouvement surtout vertical est laissé à la page, qui défile normalement sur mobile. Rien ne bouge tout seul.
Le cadre suit la largeur de son conteneur jusqu'à 640 px et garde un rapport 16/10 (aspect-ratio). Pour vos photos, placez une balise <img> dans la diapositive : elle est recadrée en object-fit: cover et la légende .bsld-caption passe par-dessus. Les dégradés violets ne servent qu'à la démonstration.
Chaque .bsld-carousel est initialisé à part : deux carrousels sur la même page ne se pilotent pas entre eux, aucune variable n'est posée sur window, et un script collé deux fois ne branche rien en double (marque data-bsld-ready).
Accessibilité
- Rôles du carrousel : le cadre est un
<div role="region">nommé (aria-label) avecaria-roledescription="carrousel"; le script donne à chaque diapositiverole="group",aria-roledescription="diapositive"et un nom « 2 sur 5 ». - Ce qui est caché l'est vraiment : les diapositives hors du cadre reçoivent
inertetaria-hidden; un lien posé sur une image masquée n'est donc pas atteint au Tab. - Annonce du changement : un paragraphe invisible en
aria-live="polite"dit « diapositive 3 sur 5 » après chaque clic, touche ou glisser ; rien n'est annoncé au chargement. - Flèches nommées : vrais
<button type="button">« Diapositive précédente » et « Diapositive suivante », anneau blanc au focus clavier, icônes SVG cachées des lecteurs d'écran. - Mouvement réduit : sous
prefers-reduced-motion: reducela piste change de place sans glisser ; les flèches, le clavier et le doigt fonctionnent toujours.
Compatibilité navigateur
Environ 3,5 Ko de JavaScript sans dépendance. Mesuré le 08/11/2026 dans Chromium, Firefox et WebKit (Playwright) : deux copies indépendantes, script collé deux fois, Tab puis Entrée, touches ← →, glisser au doigt à 390 px (Chromium) et à la souris (Firefox, WebKit), mouvement réduit, aucune erreur. Versions minimales tirées de @mdn/browser-compat-data 8.1.8 : inert fixe le plancher (Chrome 102, Firefox 112, Safari 15.5).
Avant inert (Chrome 88-101, Firefox 89-111, Safari 15-15.4) tout fonctionne, mais un lien placé dans une diapositive masquée reste atteignable au Tab. Avant Safari 15 (et avant Chrome 88, Firefox 89), aspect-ratio manque : donnez une hauteur avec --bsld-height. Sans JavaScript, la première diapositive reste affichée et les flèches ne font rien.
Le code
Copiez les trois blocs dans votre page. Aucune dépendance.
<div class="fx-0161-root">
<div class="bsld-carousel" role="region" aria-roledescription="carrousel" aria-label="Diaporama" data-slide="diapositive" data-of="sur">
<div class="bsld-track">
<div class="bsld-slide"><span class="bsld-caption">Slide 1</span></div>
<div class="bsld-slide"><span class="bsld-caption">Slide 2</span></div>
<div class="bsld-slide"><span class="bsld-caption">Slide 3</span></div>
<div class="bsld-slide"><span class="bsld-caption">Slide 4</span></div>
<div class="bsld-slide"><span class="bsld-caption">Slide 5</span></div>
</div>
<button type="button" class="bsld-arrow bsld-prev" aria-label="Diapositive précédente">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<button type="button" class="bsld-arrow bsld-next" aria-label="Diapositive suivante">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
</div>
.fx-0161-root,
.fx-0161-root *,
.fx-0161-root *::before,
.fx-0161-root *::after {
box-sizing: border-box;
}
/* Cadre de démonstration : fond, marge intérieure et hauteur minimale réglables */
.fx-0161-root {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: var(--bsld-stage-min-height, 250px);
padding: var(--bsld-stage-padding, 25px 16px);
background: var(--bsld-stage-bg, #1a1a24);
color: #fff;
font-family: sans-serif;
}
/* Le carrousel prend la largeur de son conteneur, jusqu'à --bsld-width ; hauteur = largeur × ratio */
.bsld-carousel {
position: relative;
width: 100%;
max-width: var(--bsld-width, 640px);
height: var(--bsld-height, auto);
aspect-ratio: var(--bsld-ratio, 16 / 10);
overflow: hidden;
overflow: clip; /* le cadre ne peut pas défiler tout seul (focus, scrollIntoView) */
border-radius: var(--bsld-radius, 1rem);
background: #12121a;
}
.bsld-track {
display: flex;
height: 100%;
transition: transform var(--bsld-duration, 0.5s) ease-out;
touch-action: pan-y; /* le doigt fait défiler la page à la verticale, le carrousel à l'horizontale */
user-select: none;
-webkit-user-select: none;
}
.bsld-slide {
position: relative;
flex: 0 0 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
/* Vos photos : <img> dans la diapositive, recadrée pour la remplir */
.bsld-slide > img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
-webkit-user-drag: none;
}
.bsld-caption {
position: relative;
font-weight: 600;
font-size: 1.2rem;
color: #fff;
text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35);
}
/* Fonds de démonstration (remplacés par vos images) */
.bsld-slide:nth-child(1) { background: linear-gradient(135deg, #6366f1, #8b5cf6); }
.bsld-slide:nth-child(2) { background: linear-gradient(135deg, #8b5cf6, #d946ef); }
.bsld-slide:nth-child(3) { background: linear-gradient(135deg, #d946ef, #f43f5e); }
.bsld-slide:nth-child(4) { background: linear-gradient(135deg, #f43f5e, #f97316); }
.bsld-slide:nth-child(5) { background: linear-gradient(135deg, #f97316, #eab308); }
.bsld-arrow {
position: absolute;
top: 50%;
z-index: 10;
width: 36px;
height: 36px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
transform: translateY(-50%);
background: var(--bsld-arrow-bg, rgba(255, 255, 255, 0.2));
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
cursor: pointer;
transition: background 0.3s ease, transform 0.3s ease;
}
.bsld-arrow:hover {
background: rgba(255, 255, 255, 0.4);
transform: translateY(-50%) scale(1.1);
}
.bsld-arrow:focus-visible {
outline: 2px solid #fff;
outline-offset: 2px;
}
.bsld-arrow svg {
width: 18px;
height: 18px;
stroke: #fff;
stroke-width: 2;
fill: none;
}
.bsld-prev { left: 10px; }
.bsld-next { right: 10px; }
/* Texte lu par les lecteurs d'écran seulement */
.bsld-status {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
/* Mouvement réduit : le changement de diapositive reste, mais sans glissement */
@media (prefers-reduced-motion: reduce) {
.bsld-track,
.bsld-arrow {
transition: none;
}
}
(function () {
// Basic Slider : la piste glisse d'une diapositive à l'autre (flèches, touches ← →, glisser au doigt ou à la souris).
// Chaque .bsld-carousel de la page a sa propre instance ; rien n'est déclaré sur window.
function init(car) {
if (car.hasAttribute('data-bsld-ready')) return; // script collé deux fois : rien n'est branché deux fois
var track = car.querySelector('.bsld-track');
var slides = track ? track.querySelectorAll('.bsld-slide') : [];
if (!slides.length) return;
car.setAttribute('data-bsld-ready', '');
var prev = car.querySelector('.bsld-prev');
var next = car.querySelector('.bsld-next');
var word = car.getAttribute('data-slide') || 'slide';
var of = car.getAttribute('data-of') || 'of';
var n = slides.length;
var index = 0;
// annonce « diapositive 2 sur 5 » aux lecteurs d'écran après chaque changement
var status = document.createElement('p');
status.className = 'bsld-status';
status.setAttribute('aria-live', 'polite');
car.appendChild(status);
for (var i = 0; i < n; i++) {
slides[i].setAttribute('role', 'group');
slides[i].setAttribute('aria-roledescription', word);
slides[i].setAttribute('aria-label', (i + 1) + ' ' + of + ' ' + n);
}
function go(k, announce) {
index = ((k % n) + n) % n; // en boucle : après la dernière, la première
track.style.transform = 'translateX(' + (-100 * index) + '%)';
for (var i = 0; i < n; i++) {
// diapositives cachées : hors du Tab et des lecteurs d'écran
if (i === index) {
slides[i].removeAttribute('inert');
slides[i].removeAttribute('aria-hidden');
} else {
slides[i].setAttribute('inert', '');
slides[i].setAttribute('aria-hidden', 'true');
}
}
if (announce) status.textContent = (car.getAttribute('data-slide') || word) + ' ' + (index + 1) + ' ' + (car.getAttribute('data-of') || of) + ' ' + n;
}
if (prev) prev.addEventListener('click', function () { go(index - 1, true); });
if (next) next.addEventListener('click', function () { go(index + 1, true); });
car.addEventListener('keydown', function (e) {
if (/^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName) || e.target.isContentEditable) return;
if (e.key === 'ArrowLeft') { e.preventDefault(); go(index - 1, true); }
if (e.key === 'ArrowRight') { e.preventDefault(); go(index + 1, true); }
});
// Glisser (Pointer Events : doigt, souris, stylet) : plus de 50 px à l'horizontale change de diapositive
var x0 = null, y0 = 0, dragged = false;
track.addEventListener('pointerdown', function (e) {
if (e.pointerType === 'mouse' && e.button !== 0) return;
x0 = e.clientX; y0 = e.clientY; dragged = false;
});
track.addEventListener('pointerup', function (e) {
if (x0 === null) return;
var dx = e.clientX - x0, dy = e.clientY - y0;
x0 = null;
if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) {
dragged = true;
go(index + (dx < 0 ? 1 : -1), true);
}
});
track.addEventListener('pointercancel', function () { x0 = null; });
// un glisser à la souris ne doit pas ouvrir le lien posé sur la diapositive
track.addEventListener('click', function (e) {
if (dragged) { e.preventDefault(); e.stopPropagation(); dragged = false; }
}, true);
track.addEventListener('dragstart', function (e) { e.preventDefault(); });
go(0, false);
}
Array.prototype.forEach.call(document.querySelectorAll('.bsld-carousel'), init);
})();
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Largeur maximale (CSS — --bsld-width sur .fx-0161-root) |
640px | Le carrousel prend toute la largeur disponible jusqu'à cette valeur. 100% pour un bandeau sans limite. |
Proportions (CSS — --bsld-ratio) |
16 / 10 | Hauteur calculée depuis la largeur. 4 / 3 pour des photos de produit, 21 / 9 pour un bandeau. |
Hauteur fixe (CSS — --bsld-height) |
auto | Une valeur (320px, 100%) remplace le calcul par les proportions ; utile dans un bloc à hauteur imposée. |
Durée du glissement (CSS — --bsld-duration) |
0.5s | Temps de la transition de la piste, en ease-out. Sans effet sous mouvement réduit. |
Arrondi et fond des flèches (CSS — --bsld-radius, --bsld-arrow-bg) |
1rem · blanc à 20 % | Sur des photos claires, foncez les flèches : --bsld-arrow-bg: rgba(0,0,0,.45). |
Cadre de démonstration (CSS — --bsld-stage-bg, --bsld-stage-padding, --bsld-stage-min-height) |
#1a1a24 · 25px 16px · 250px | Le fond et la marge autour du carrousel. Dans votre page : transparent, 0 et 0. |
Textes lus à voix haute (HTML — aria-label, data-slide, data-of sur .bsld-carousel) |
Diaporama · diapositive · sur | Nom du carrousel et mots de l'annonce « diapositive 2 sur 5 ». Les libellés des flèches se changent sur les boutons. |
Vos images (HTML — <img> dans .bsld-slide) |
dégradés de démonstration | L'image couvre la diapositive ; gardez un alt qui décrit la photo. Autant de diapositives que vous voulez : le script les compte. |
FAQ
Peut-on faire défiler le carrousel automatiquement ?
Pas dans ce code : il ne bouge que sur une action de la personne. Si vous ajoutez un minuteur qui clique sur .bsld-next, prévoyez un bouton pause, l'arrêt au survol et au focus, et rien sous prefers-reduced-motion : un défilement de plus de cinq secondes sans pause n'est pas accessible.
Pourquoi le retour de la dernière à la première diapositive traverse-t-il toutes les autres ?
La piste revient simplement à translateX(0) : le glissement repasse donc devant les diapositives intermédiaires. Pour enchaîner sans ce retour en arrière, il faut des copies aux deux bouts de la piste, ce que fait l'effet Infinite Loop de la même catégorie.
La diapositive reste-t-elle bien cadrée quand la fenêtre change de taille ?
Oui. La position est donnée en pourcentage de la largeur de la piste (translateX(-200%) pour la troisième), pas en pixels : quand la fenêtre ou la colonne change de largeur, la diapositive affichée reste en place sans aucun recalcul, et sans ResizeObserver.