Après la personnalisation des <select> et le Masonry sans JavaScript avec grid-lanes, le CSS continue sa conquête du territoire JavaScript avec une nouvelle propriété pseudo-élément : ::scroll-button(). Elle permet de générer nativement des boutons de navigation pour les conteneurs scrollables, sans écrire une seule ligne de JS.
Pourquoi c’est une vraie amélioration ?
Jusqu'ici, créer un carousel avec des boutons "précédent / suivant" imposait systématiquement du JavaScript : écouter les clics, calculer la position de scroll, gérer les états désactivés en bout de liste… Autant de code à maintenir, à tester et à rendre accessible.
Avec ::scroll-button(), le navigateur prend en charge tout ça nativement :
- Les boutons sont générés automatiquement par le CSS, sans aucun
<button>dans le HTML - L'état
:disabledest appliqué automatiquement quand le scroll atteint ses limites - La navigation au clavier et l'accessibilité sont gérées par le navigateur
- Combiné à
scroll-snap-type, le défilement s'effectue snap par snap, de façon précise
Démo de carousel pur CSS avec ::scroll-button

Note : cette fonctionnalité requiert Chrome 135+ ou Edge 135+
See the Pen Carousel en pur CSS avec ::scroll-button by Tibow (@Tibow) on CodePen.
Le résultat est incroyablement fluide et convaincant. Voyons maintenant les compatibilités navigateurs et comment marche cette petite merveille en détails !
Compatibilité avec les navigateurs modernes
::scroll-button() est une propriété très récente et prometteuse pour 2026, et elle est supportée (pour le moment) uniquement par Chrome et Edge 135 +

Bonne pratique : la dégradation est gracieuse. Sur les navigateurs non compatibles, le conteneur reste scrollable (touch, trackpad, barre de défilement) — les boutons disparaissent simplement. Pas d'erreur, pas de JavaScript à prévoir.
Comment ça fonctionne : sémantique et balisage
Le HTML : minimaliste
Aucun bouton à ajouter dans le markup. Le conteneur scroll suffit :
<div class="carousel">
<article class="card"><!-- contenu --></article>
<article class="card"><!-- contenu --></article>
<article class="card"><!-- contenu --></article>
</div>
Le CSS : tout se passe ici
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
}
/* Bouton "précédent" */
.carousel::scroll-button(inline-start) {
content: "←"; /* OBLIGATOIRE pour afficher le bouton */
display: flex;
width: 3rem;
height: 3rem;
background: #1a1612;
color: white;
border-radius: 50%;
cursor: pointer;
}
/* Bouton "suivant" */
.carousel::scroll-button(inline-end) {
content: "→";
/* mêmes styles */
}
Les valeurs acceptées par ::scroll-button()
La propriété accepte 4 directions correspondant aux axes logiques CSS :
| Valeur | Direction |
|---|---|
inline-start | ← (début de ligne, gauche en LTR) |
inline-end | → (fin de ligne, droite en LTR) |
block-start | ↑ (début de bloc, haut) |
block-end | ↓ (fin de bloc, bas) |
Cela permet de gérer aussi bien les scrolls horizontaux que verticaux, et les contextes RTL (arabe, hébreu) sans aucune adaptation CSS.
La propriété content est obligatoire
Comme pour ::before et ::after, la propriété content est indispensable pour que le pseudo-élément s'affiche. Sans elle, le bouton n'est pas rendu.
/* ❌ Ne s'affiche pas */
.carousel::scroll-button(inline-end) {
background: black;
}
/* ✅ S'affiche */
.carousel::scroll-button(inline-end) {
content: "→";
background: black;
}
Gérer les états :disabled et :hover
Le navigateur applique automatiquement :disabled quand le scroll ne peut plus aller dans une direction. On peut cibler cet état directement :
.carousel::scroll-button(inline-start):disabled,
.carousel::scroll-button(inline-end):disabled {
opacity: 0.2;
pointer-events: none;
}
.carousel::scroll-button(inline-end):hover {
background: #c8502a;
}
Bonus : les points de pagination avec ::scroll-marker
Dans la même spec, ::scroll-marker permet de générer des indicateurs de position (les fameux "dots") eux aussi sans JavaScript :
/* Groupe de dots généré automatiquement */
.carousel::scroll-marker-group {
display: flex;
gap: 0.5rem;
justify-content: center;
}
/* Un dot par élément */
.card::scroll-marker {
content: "";
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background: #ccc;
}
/* Dot actif */
.card::scroll-marker:target-current {
background: #c8502a;
width: 1.5rem;
border-radius: 0.25rem;
}
Conclusion
::scroll-button() s'inscrit dans une tendance de fond : le CSS reprend progressivement des interactions que seul JavaScript pouvait gérer jusqu'ici. Après les animations complexes, les transitions de vue avec @view-transition, ou encore la personnalisation des éléments de formulaire, c'est maintenant la navigation des conteneurs scrollables qui bascule côté CSS natif.
Le résultat : un HTML plus sémantique, moins de JavaScript à maintenir, de meilleures performances et une accessibilité gérée nativement par le navigateur.
C'est aujourd'hui limité à Chrome 135+, mais la spec est en bonne voie et Firefox a déjà annoncé son implémentation. À surveiller de près, et à tester dès maintenant sur vos projets CodePen !
Laisser un commentaire