compare-surface
Before/after image comparison slider with drag handle, keyboard support, and progressive enhancement.
Overview
The <compare-surface> component creates a before/after comparison slider. Two children overlay each other, and a draggable divider reveals one side. Works with any two children — images, divs, or any other elements.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
data-position |
number | 50 |
Initial slider position from 0 (fully left) to 100 (fully right). |
Custom Start Position
Set data-position to control where the divider starts. A value of 25 means the divider starts 25% from the left, showing mostly the “after” content.
Non-Image Content
The component works with any two child elements, not just images. Use divs, text blocks, or any HTML content.
Draft
The original version before revisions.
Final
The polished version with improved clarity.
Keyboard Navigation
| Key | Action |
|---|---|
| ArrowLeft / ArrowDown | Move divider 1% left |
| ArrowRight / ArrowUp | Move divider 1% right |
| Shift + Arrow | Move divider 10% in the arrow direction |
Events
| Event | Detail | Description |
|---|---|---|
compare-surface:change |
{ position: number } |
Fired when the slider position changes (drag or keyboard). |
Accessibility
Slider Role
The divider has role="slider" with aria-valuemin, aria-valuemax, and aria-valuenow. Screen readers announce position changes as the slider moves.
Keyboard Support
The divider is focusable (tabindex="0") and responds to arrow keys. Shift + arrow provides larger steps for faster navigation.
Touch Support
The divider uses touch-action: none and setPointerCapture for reliable touch/pointer drag behavior across devices.
Styling
The divider and handle can be styled with CSS. The divider is a .comparison-divider element with a ::after pseudo-element for the circular handle.
Progressive Enhancement
Without JavaScript, both children display side-by-side in a natural grid. The :not(:defined) selector provides the fallback layout. Once JS registers the component, children overlap and the clip-path slider takes over.
Related Elements
<content-swap>— Two-face content toggle with transitions<tabs-wc>— Tab panels for content switching