medias · comparaison · slider

Slider avant / après

Curseur à glisser pour révéler progressivement l'image 'après' par-dessus l'image 'avant'. Utilise un <input type="range"> natif (accessible au clavier), avec un tout petit script pour synchroniser l'affichage.

Voir le code source
<style>
.bn-compare-slider {
  padding: 1rem;
  background: #0B0F10;
  font-family: 'Inter', sans-serif;
}

.bn-compare-slider__stage {
  position: relative;
  overflow: hidden;
  border-radius: 4px;

  /* Cette variable est mise à jour par script.js à chaque
     déplacement du curseur — c'est elle qui pilote à la fois la
     découpe de l'image "après" et la position du repère visuel */
  --reveal: 50%;
}

.bn-compare-slider__before,
.bn-compare-slider__after-wrap {
  display: block;
  width: 100%;
}

.bn-compare-slider__before img,
.bn-compare-slider__before {
  width: 100%;
  display: block;
}

.bn-compare-slider__after-wrap {
  position: absolute;
  inset: 0;
  /* On ne montre que la partie gauche de l'image "après", jusqu'à
     la position du curseur — c'est ça qui crée l'effet de révélation */
  clip-path: inset(0 calc(100% - var(--reveal)) 0 0);
}

.bn-compare-slider__after {
  width: 100%;
  display: block;
}

.bn-compare-slider__handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--reveal);
  transform: translateX(-50%);

  width: 2px;
  background: #E6E4DD;
  pointer-events: none;
}

.bn-compare-slider__handle span {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);

  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #E6E4DD;
  color: #0B0F10;

  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
}

.bn-compare-slider__label {
  position: absolute;
  top: 16px;
  padding: 0.3rem 0.8rem;
  background: rgba(5, 8, 8, 0.7);
  border-radius: 999px;
  color: #E6E4DD;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  pointer-events: none;
}

.bn-compare-slider__label--left { left: 16px; }
.bn-compare-slider__label--right { right: 16px; }

/* Le vrai <input type="range"> : rendu invisible mais toujours
   fonctionnel, étiré sur toute la zone pour capter le clic/glisser
   n'importe où sur l'image (pas juste sur une petite poignée) */
.bn-compare-slider__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
}

</style>

<div class="bn-compare-slider" data-fx-root>
  <div class="bn-compare-slider__stage">
    <img class="bn-compare-slider__before" src="https://placehold.co/900x500/1D2324/E6E4DD?text=AVANT" alt="Avant retouche" />
    <div class="bn-compare-slider__after-wrap">
      <img class="bn-compare-slider__after" src="https://placehold.co/900x500/C2410C/0B0F10?text=APRÈS" alt="Après retouche" />
    </div>

    <div class="bn-compare-slider__handle" aria-hidden="true">
      <span>↔</span>
    </div>

    <span class="bn-compare-slider__label bn-compare-slider__label--left">Avant</span>
    <span class="bn-compare-slider__label bn-compare-slider__label--right">Après</span>

    <!-- Le vrai contrôle, natif et accessible au clavier (flèches
         gauche/droite) — visuellement invisible, posé par-dessus
         toute l'image pour qu'on puisse cliquer/glisser n'importe où -->
    <input
      type="range"
      class="bn-compare-slider__input"
      min="0"
      max="100"
      value="50"
      aria-label="Curseur de comparaison avant/après"
    />
  </div>
</div>


<script>
/* ==========================================================
   SLIDER AVANT / APRÈS
   Le <input type="range"> fait tout le travail d'interaction
   (glisser, cliquer, flèches du clavier). Ce script se contente de
   reporter sa valeur (0 à 100) sur une variable CSS "--reveal",
   que le CSS utilise pour découper l'image "après" et positionner
   le repère visuel.
   ========================================================== */

(function () {
  const root = document.querySelector('.bn-compare-slider[data-fx-root]');
  if (!root) return;

  const input = root.querySelector('.bn-compare-slider__input');
  const stage = root.querySelector('.bn-compare-slider__stage');

  function update() {
    stage.style.setProperty('--reveal', `${input.value}%`);
  }

  input.addEventListener('input', update);
  update();
})();

</script>