Image Comparison Slider
A draggable before/after slider with smooth pointer, touch, and full keyboard control. Copy the prompt, paste it, ship it.
Live demo
Drag the handle, click anywhere on the image, or focus the slider and use arrow keys to compare. Works with mouse, touch, and stylus.
The Prompt
Build a before/after image comparison slider using vanilla HTML, CSS, and JavaScript. No libraries, no frameworks — zero dependencies.
VISUAL DESIGN
- Two images stacked in a fixed-aspect container (16:9 default, configurable)
- Container: border-radius 12px, overflow hidden, box-shadow 0 20px 60px rgba(0,0,0,0.5), background #0a0a0a
- Top image is revealed/hidden via clip-path: inset(0 [100 - position]% 0 0) — positioned by a vertical divider line
- Vertical divider line: 2px wide, white at 0.9 opacity, full height, with a subtle drop shadow for legibility on any image
- Drag handle: 44px circular button centered on the divider, white 2px border, dark translucent background (rgba(10,10,10,0.85)) with backdrop-blur, two 14px chevron icons pointing left and right with a 2px gap between them
- Handle hover/focus: scale to 1.08 with cyan focus ring (0 0 0 4px rgba(0,196,255,0.25))
- Optional corner labels: pill-shaped badges in opposite corners ("Before" top-left, "After" top-right) — pointer-events none so they don't block dragging
INTERACTION
- Pointer Events API only (covers mouse + touch + pen) using setPointerCapture for reliable drag tracking even when the cursor leaves the container
- pointerdown anywhere on the container snaps the slider to that x position AND begins a drag
- pointermove updates the divider position, clamped between 0% and 100%
- pointerup ends the drag and releases pointer capture
- touch-action: none on the container to prevent the browser from hijacking horizontal swipes
- user-select: none on the container to prevent text-selection flicker during drag
- During drag, set data-dragging="true" on the container; CSS uses this to disable any clip-path transition for instant tracking
KEYBOARD ACCESSIBILITY
- Handle is a button with role="slider", aria-valuemin="0", aria-valuemax="100", aria-valuenow updated live, aria-label
- Arrow Left / Arrow Right: move 1% per press
- Shift + Arrow Left/Right: move 10% per press
- Home: jump to 0%, End: jump to 100%
- Page Up / Page Down: ±10%
- Focus visible ring on the handle when keyboard-navigated (use :focus-visible)
LAYOUT & RESPONSIVENESS
- Container fills its parent width up to a max-width
- Aspect ratio held with CSS aspect-ratio property (no JS height calculation)
- Both images use object-fit: cover (or preserveAspectRatio="xMidYMid slice" for inline SVG) so they crop identically
- On screens under 640px: handle shrinks to 38px, label badges shrink to 10px font
API
- Initialize via class hook: any element with data-image-compare auto-upgrades, OR a function: createImageCompare(element, { initial: 50, onChange: (pct) => ... })
- Public methods on the returned controller: setPosition(pct), getPosition(), destroy()
- onChange callback fires with the current percentage (0-100) on every update
ACCESSIBILITY
- prefers-reduced-motion: disable the handle's hover transition
- Both images need meaningful alt text (or aria-hidden if purely decorative)
- The divider and labels are aria-hidden — only the slider button is exposed to assistive tech
OUTPUT
- Single HTML file with embedded <style> and <script>
- Two sample images (or inline SVGs) labeled before/after
- Slider initialized at 50%
- Polished, professional aesthetic — should feel like the comparison sliders on premium image-editing landing pages
- CopyThe full prompt, one click.
- PasteInto Cursor, Claude Code, or any agent.
- CompareAgainst the live demo above.
What’s Inside
Stacked Image Layers
Two absolutely-positioned images sharing the same frame, the top one revealed by clip-path inset.
Clip-Path Reveal
A single CSS property animates the visible portion — no canvas, no masking image, no JavaScript redraw.
Divider Line
A 2px white seam with a soft drop-shadow that reads cleanly over both light and dark imagery.
Drag Handle
A circular grip with chevron icons centered on the divider, scaling on hover and focus.
Pointer Events
One unified API for mouse, touch, and stylus — with pointer capture for reliable off-container tracking.
Click-to-Snap
Tapping anywhere on the image jumps the divider to that position and starts a drag in one motion.
Keyboard Slider
Full ARIA slider semantics — arrow keys move 1%, shift jumps 10%, Home/End snap to the edges.
Corner Labels
Optional Before/After pill badges in opposing corners with pointer-events none so they never block input.
Usage Guidelines
Use This When
- Showcasing AI-edited photos, product retouches, or filter effects on image-tool landing pages
- Documenting design redesigns, dark/light mode comparisons, or wireframe-to-final mockups in case studies
- Contractors and architects showing before/after renovation, restoration, or build progress
Not Ideal When
- The two images differ in size, crop, or aspect ratio — alignment will be visually jarring
- Comparing more than two states — use a tabbed gallery or carousel instead
- The difference is subtle and only visible side-by-side — a static split layout reads more clearly