medias · comparaison · slider
Image 1 en dessous, image 2 au-dessus : le curseur découpe la couche du dessus pour révéler celle du dessous. Version format carte — voir 'Slider avant/après' pour la version plein écran.
<style>
.bn-compare-card {
max-width: 500px;
padding: 1rem;
font-family: 'Inter', sans-serif;
background: #0B0F10;
}
.bn-compare-card__stage {
position: relative;
overflow: hidden;
border-radius: 4px;
/* Mise à jour par script.js à chaque déplacement du curseur :
pilote à la fois la découpe de la couche 2 et la position du
repère visuel */
--reveal: 50%;
}
.bn-compare-card__layer1 {
width: 100%;
display: block;
}
/* La couche 2 est positionnée exactement par-dessus la couche 1,
puis "coupée" avec clip-path pour ne montrer que sa partie gauche,
jusqu'à la position du curseur */
.bn-compare-card__layer2-wrap {
position: absolute;
inset: 0;
clip-path: inset(0 calc(100% - var(--reveal)) 0 0);
}
.bn-compare-card__layer2 {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.bn-compare-card__handle {
position: absolute;
top: 0;
bottom: 0;
left: var(--reveal);
transform: translateX(-50%);
width: 2px;
background: #E6E4DD;
pointer-events: none;
}
.bn-compare-card__handle span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 36px;
height: 36px;
border-radius: 50%;
background: #E6E4DD;
color: #0B0F10;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
}
.bn-compare-card__label {
position: absolute;
top: 12px;
padding: 0.25rem 0.7rem;
background: rgba(5, 8, 8, 0.7);
border-radius: 999px;
color: #E6E4DD;
font-size: 0.65rem;
letter-spacing: 0.08em;
text-transform: uppercase;
pointer-events: none;
}
.bn-compare-card__label--left { left: 12px; }
.bn-compare-card__label--right { right: 12px; }
/* Contrôle natif invisible, étiré sur toute la zone pour capter le
clic/glisser n'importe où sur l'image */
.bn-compare-card__input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: ew-resize;
}
.bn-compare-card__caption {
margin: 0.75rem 0 0;
color: #979793;
font-size: 0.8rem;
}
</style>
<div class="bn-compare-card" data-fx-root>
<div class="bn-compare-card__stage">
<!-- Couche 1 (dessous) : l'image "avant", toujours entièrement visible -->
<img class="bn-compare-card__layer1" src="https://placehold.co/700x460/1D2324/E6E4DD?text=AVANT" alt="Avant retouche" />
<!-- Couche 2 (dessus) : l'image "après", découpée par le curseur -->
<div class="bn-compare-card__layer2-wrap">
<img class="bn-compare-card__layer2" src="https://placehold.co/700x460/C2410C/0B0F10?text=APRÈS" alt="Après retouche" />
</div>
<div class="bn-compare-card__handle" aria-hidden="true">
<span>↔</span>
</div>
<span class="bn-compare-card__label bn-compare-card__label--left">Avant</span>
<span class="bn-compare-card__label bn-compare-card__label--right">Après</span>
<!-- Le vrai contrôle, natif et accessible au clavier -->
<input
type="range"
class="bn-compare-card__input"
min="0"
max="100"
value="50"
aria-label="Curseur de comparaison avant/après"
/>
</div>
<p class="bn-compare-card__caption">Retouche colorimétrique & contraste</p>
</div>
<script>
/* ==========================================================
COMPARAISON AVANT / APRÈS
Même principe que le slider avant/après plein écran : le
<input type="range"> pilote une variable CSS "--reveal" qui
découpe la couche du dessus (l'image "après") pour révéler
la couche du dessous (l'image "avant").
========================================================== */
(function () {
const root = document.querySelector('.bn-compare-card[data-fx-root]');
if (!root) return;
const input = root.querySelector('.bn-compare-card__input');
const stage = root.querySelector('.bn-compare-card__stage');
function update() {
stage.style.setProperty('--reveal', `${input.value}%`);
}
input.addEventListener('input', update);
update();
})();
</script>