Components / Motion / Comparison Slider
Comparison Slider beta
Before and after, one image wide: drag the seam and the second state wipes over the first.
One scene, drawn twice — flat zinc before, lit after. Drag, or focus and use the arrow keys.
<div class="k-cmp" data-cmp> <div class="layer"><svg viewBox="0 0 400 250" width="100%" height="100%" preserveAspectRatio="none" aria-hidden="true" style="display:block"> <rect width="400" height="250" style="fill:var(--k-accent-bg-strong)"/> <circle cx="322" cy="52" r="24" style="fill:var(--k-core)"/> <path d="M0 178 L110 108 L215 178 L300 128 L400 188 L400 250 L0 250 Z" style="fill:var(--k-zinc-600)"/> <path d="M0 208 L140 148 L265 208 L345 168 L400 202 L400 250 L0 250 Z" style="fill:var(--k-zinc-800)"/> <rect x="52" y="168" width="46" height="34" style="fill:var(--k-zinc-100)"/> <path d="M46 168 L75 148 L104 168 Z" style="fill:var(--k-ink)"/> </svg></div> <div class="layer top"><svg viewBox="0 0 400 250" width="100%" height="100%" preserveAspectRatio="none" aria-hidden="true" style="display:block"> <rect width="400" height="250" style="fill:var(--k-zinc-200)"/> <circle cx="322" cy="52" r="24" style="fill:var(--k-zinc-400)"/> <path d="M0 178 L110 108 L215 178 L300 128 L400 188 L400 250 L0 250 Z" style="fill:var(--k-zinc-300)"/> <path d="M0 208 L140 148 L265 208 L345 168 L400 202 L400 250 L0 250 Z" style="fill:var(--k-zinc-400)"/> <rect x="52" y="168" width="46" height="34" style="fill:var(--k-zinc-500)"/> <path d="M46 168 L75 148 L104 168 Z" style="fill:var(--k-zinc-600)"/> </svg></div> <span class="tag l">BEFORE</span><span class="tag r">AFTER</span> <div class="seam"></div> <div class="grip"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="m11 8-4 4 4 4M13 8l4 4-4 4"/></svg></div> <input type="range" min="0" max="100" value="55" aria-label="Share of the before image shown"> </div> <p style="font-size:12.5px;color:var(--k-ink-3);margin-top:9px">One scene, drawn twice — flat zinc before, lit after. Drag, or focus and use the arrow keys.</p>
/* KATA Comparison Slider — two states, one seam. */
.k-cmp{--pos:55%;width:100%;position:relative;overflow:hidden;border-radius:var(--k-radius-lg);border:1px solid var(--k-line);font-family:var(--k-font);max-width:520px;aspect-ratio:8/5;background:var(--k-bg-soft)}
.k-cmp .layer{position:absolute;inset:0}
.k-cmp .top{clip-path:inset(0 calc(100% - var(--pos)) 0 0)}
.k-cmp .tag{position:absolute;top:10px;font-size:11px;font-weight:600;letter-spacing:.06em;padding:3px 9px;border-radius:99px;background:rgba(24,24,27,.72);color:var(--k-on-color)}
.k-cmp .tag.l{left:10px}
.k-cmp .tag.r{right:10px}
.k-cmp .seam{position:absolute;top:0;bottom:0;left:var(--pos);width:2px;margin-left:-1px;background:var(--k-on-color);box-shadow:0 0 0 1px rgba(24,24,27,.25)}
.k-cmp .grip{position:absolute;top:50%;left:var(--pos);transform:translate(-50%,-50%);width:38px;height:38px;border-radius:50%;background:var(--k-on-color);color:var(--k-ink);display:flex;align-items:center;justify-content:center;box-shadow:0 2px 10px rgba(16,24,40,.3);pointer-events:none}
.k-cmp input[type=range]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:ew-resize;margin:0}
// KATA Comparison Slider — the seam is a range input wearing the picture.
document.querySelectorAll('[data-cmp]').forEach(function (el) {
var range = el.querySelector('input[type=range]');
function set() { el.style.setProperty('--pos', range.value + '%'); }
range.addEventListener('input', set);
set();
});
Installation
CLI
node kata-ui.mjs add comparison-slider
Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.
Manual
- Load
tokens.cssonce, before any part. - Copy the CSS tab into your stylesheet.
- Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.
Usage
For redesigns, retouching and day-versus-night — two states of the same picture, divided by a seam the reader moves. Both layers are the same scene drawn twice here; with photographs, use the same image twice and let a filter be the difference.
Accessibility
The seam is a real slider: arrow keys move it for people who cannot drag, and its label says what it divides ("Share of the before image shown"). The pictures themselves still need their own alt text.
Anatomy
The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.
.k-cmp | class |
.layer | class |
.top | class |
.tag | class |
.l | class |
.r | class |
.seam | class |
.grip | class |
data-cmp | data hook |
aria-hidden | ARIA |
aria-label | ARIA |
Related parts
Accordion Gallery beta
Five scenes in a row; the one you touch opens up and tells its caption.
Aurora beta
A slow wash of color behind a hero — the accent and the core, drifting under glass.
Beam border beta
A single point of light travels the edge of a card. The beam is the core color — the one accent, in motion.
Blur In beta
Words arrive out of focus and sharpen one after another, like eyes adjusting.