medias · comparaison · slider
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.
<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>