Draggable divider comparing two images.
<div class="ba-slider" style="--pos:50%;--accent:#BFD9F2;">
<div class="ba-slider__frame">
<div class="ba-slider__img ba-slider__img--after" aria-hidden="true">
<svg viewBox="0 0 400 240" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#A8E6C1"/><circle cx="300" cy="80" r="50" fill="#FAF7F2"/></svg>
<span class="ba-slider__tag">After</span>
</div>
<div class="ba-slider__img ba-slider__img--before" aria-hidden="true">
<svg viewBox="0 0 400 240" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#241430"/><circle cx="300" cy="80" r="50" fill="#D9C8F0"/></svg>
<span class="ba-slider__tag">Before</span>
</div>
<div class="ba-slider__handle"></div>
</div>
<!-- In production, sync an input[type=range] with this element's --pos custom property on the "input" event to make the divider live-draggable. -->
<input class="ba-slider__range" type="range" min="0" max="100" value="50" aria-label="Comparison position between Before and After" />
</div>
<style>
.ba-slider { max-width: 360px; font: 12px/1.4 system-ui, sans-serif; color: #241430; }
.ba-slider__frame { position: relative; aspect-ratio: 5 / 3; border-radius: 12px; overflow: hidden; border: 1px solid #24143022; }
.ba-slider__img { position: absolute; inset: 0; }
.ba-slider__img svg { display: block; width: 100%; height: 100%; }
.ba-slider__img--before { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
.ba-slider__tag { position: absolute; top: 8px; left: 8px; padding: 2px 8px; border-radius: 6px; background: #241430cc; color: #FAF7F2; font-weight: 600; }
.ba-slider__handle { position: absolute; top: 0; bottom: 0; left: var(--pos); width: 3px; background: var(--accent); transform: translateX(-50%); pointer-events: none; }
.ba-slider__range { width: 100%; margin-top: 10px; accent-color: var(--accent); }
</style><div class="ba-slider-tw max-w-[360px] font-sans text-xs text-[#241430]" style="--pos:50%;--accent:#BFD9F2;">
<div class="relative overflow-hidden rounded-xl border border-[#24143022]" style="aspect-ratio:5/3;">
<div class="absolute inset-0" aria-hidden="true">
<svg viewBox="0 0 400 240" class="block w-full h-full" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#A8E6C1"/><circle cx="300" cy="80" r="50" fill="#FAF7F2"/></svg>
<span class="absolute top-2 left-2 px-2 py-0.5 rounded-md font-semibold text-[#FAF7F2]" style="background:#241430cc;">After</span>
</div>
<div class="absolute inset-0" style="clip-path:inset(0 calc(100% - var(--pos)) 0 0);" aria-hidden="true">
<svg viewBox="0 0 400 240" class="block w-full h-full" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#241430"/><circle cx="300" cy="80" r="50" fill="#D9C8F0"/></svg>
<span class="absolute top-2 left-2 px-2 py-0.5 rounded-md font-semibold text-[#FAF7F2]" style="background:#241430cc;">Before</span>
</div>
<div class="absolute top-0 bottom-0 w-[3px] pointer-events-none" style="left:var(--pos);background:var(--accent);transform:translateX(-50%);"></div>
</div>
<!-- In production, sync an input[type=range] with this element's --pos custom property on the "input" event to make the divider live-draggable. -->
<input class="w-full mt-2.5" style="accent-color:var(--accent);" type="range" min="0" max="100" value="50" aria-label="Comparison position between Before and After" />
</div>export function BeforeAfterSlider({
beforeLabel = "Before",
afterLabel = "After",
position = 50,
accent = "#BFD9F2",
}) {
return (
<div style={{ maxWidth: 360, font: "12px/1.4 system-ui, sans-serif", color: "#241430",
"--pos": `${position}%`, "--accent": accent }}>
<div style={{ position: "relative", overflow: "hidden", borderRadius: 12, border: "1px solid #24143022", aspectRatio: "5 / 3" }}>
<div style={{ position: "absolute", inset: 0 }} aria-hidden="true">
<svg viewBox="0 0 400 240" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#A8E6C1" /><circle cx="300" cy="80" r="50" fill="#FAF7F2" /></svg>
<span style={{ position: "absolute", top: 8, left: 8, padding: "2px 8px", borderRadius: 6, background: "#241430cc", color: "#FAF7F2", fontWeight: 600 }}>{afterLabel}</span>
</div>
<div style={{ position: "absolute", inset: 0, clipPath: `inset(0 calc(100% - ${position}%) 0 0)` }} aria-hidden="true">
<svg viewBox="0 0 400 240" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"><rect width="400" height="240" fill="#241430" /><circle cx="300" cy="80" r="50" fill="#D9C8F0" /></svg>
<span style={{ position: "absolute", top: 8, left: 8, padding: "2px 8px", borderRadius: 6, background: "#241430cc", color: "#FAF7F2", fontWeight: 600 }}>{beforeLabel}</span>
</div>
<div style={{ position: "absolute", top: 0, bottom: 0, left: `${position}%`, width: 3, background: accent, transform: "translateX(-50%)", pointerEvents: "none" }} />
</div>
{/* In production, wire this range's onChange to update `position` state so the divider is live-draggable. */}
<input
type="range"
min={0}
max={100}
defaultValue={position}
aria-label={`Comparison position between ${beforeLabel} and ${afterLabel}`}
style={{ width: "100%", marginTop: 10, accentColor: accent }}
/>
</div>
);
}