What's included
A before/after slider, in two files: a stylesheet and a script. No dependencies, no build step, no external requests — you copy them into a project and it works. The two demo images come with it.
The component doesn't reinvent a slider: it sits on a browser
<input type="range">, stretched across the whole image and painted transparent. That's what receives the finger, the mouse and the keyboard; that's what carries the
sliderrole, its bounds and its value for screen readers. The script adds one thing only: it copies that value into a CSS variable.
Features
- Full keyboard support, without a line of code — arrows, Page Up and Page Down, Home and End: it all comes from the native slider, so nothing can drift out of step with the platform.
- Vertical direction — one class is enough. It's the browser that turns the control: the gesture, the arrow keys and the announced orientation all follow by themselves.
- The two images stay aligned — the reveal is a clip, not a resize. No position, not even 1%, shifts them out of register.
- It tightens up according to the room it gets — not according to the size of the screen. Dropped into a narrow column on a wide page, it still reduces its spacing.
- One variable for the scale — frame, corner radius, handle, chevrons and labels all derive from the text size and follow together.
- The page keeps scrolling — on mobile, the component takes the horizontal gesture and leaves the vertical one to a finger that simply wants to scroll.
Dropping it into a page
Two files to load, then the markup:
<link rel="stylesheet" href="compare-slider.css">
<script src="compare-slider.js" defer></script>
<div class="compare-slider">
<div class="compare-slider__frame">
<img class="compare-slider__image"
src="apres.webp" alt="After">
<div class="compare-slider__reveal">
<img class="compare-slider__image"
src="avant.webp" alt="Before">
</div>
<span class="compare-slider__label
compare-slider__label--before">Before</span>
<span class="compare-slider__label
compare-slider__label--after">After</span>
<span class="compare-slider__line" aria-hidden="true"></span>
<span class="compare-slider__handle" aria-hidden="true">
<svg class="compare-slider__handle-icon"
viewBox="0 0 24 24" aria-hidden="true">
<path d="M9 6 L4 12 L9 18 M15 6 L20 12 L15 18"/>
</svg>
</span>
<input class="compare-slider__control" type="range"
min="0" max="100" step="any" value="50"
aria-label="Comparison position">
</div>
</div>
No attribute to remember: the component installs itself on page load on every .compare-slider, including those added later. Add the
compare-slider--vertical class for the vertical direction,
compare-slider--paper for the light background.
What you should know
-
Without JavaScript, it appears but no longer moves.
The two images, the labels and the seam all stay there, at the position set in CSS. The slider itself is removed: there's no way to tie a
rangeto a CSS variable without a script, and a control that moves without moving anything is worth less than no control at all. -
The stage wants a height. By default it takes the reference's own; give it
--cs-block-size: 100%so it fills its parent, provided that parent has one. -
No inline styles, no inline scripts. The component honours the site's strict security policy, and never uses
innerHTML. - The design comes from elsewhere. The proportions follow those of a commercial Framer component, measured on the published site at five widths. The code itself is original — the technique isn't even the same — and the two photographs, a room before and after renovation, were generated for this folder.
Take it, open it, break it.
This component is free. The full library — the templates and the other components — comes with the subscription, alongside the course.
Immediate access · No commitment · Cancel in one click