Build a Responsive Before-and-After Slider with HTML, CSS and JavaScript
A before-and-after comparison can help explain a website redesign. Visitors can inspect both versions in the same space instead of trying to remember one screenshot while looking at another.
I built a small comparison for my CodePen profile using HTML, CSS and vanilla JavaScript. The preview shows two fictional garden-studio websites. It is a design exercise, not a client result or evidence of higher conversions.
Open the working CodePen demo.
How the before-and-after slider works
Both designs occupy the same area. The first stays underneath. CSS clips the second design, and a native range input controls how much of it appears.
I kept the control below the preview. That gives touch users a separate place to drag without covering the design with their finger. The round handle inside the preview is a visual indicator, not a second draggable control.
The example below is a smaller version of the CodePen demo. Save it as index.html and open it in a browser. It needs no build step or external library.
Add the HTML structure
The comparison needs a preview container, two layers and a labelled range input. Preset buttons let visitors jump to either complete design or return to the middle.
<main>
<h1>A website redesign, compared</h1>
<div class="preview" role="img"
aria-label="Fictional garden websites: a generic welcome compared with specific garden services.">
<section class="layer before" aria-hidden="true">
<p>BEFORE / GARDEN STUDIO</p>
<h2>Welcome to our website.</h2>
<p>Professional solutions for your garden needs.</p>
</section>
<section class="layer after" id="after" aria-hidden="true">
<p>AFTER / GARDEN STUDIO</p>
<h2>A garden that fits the way you live.</h2>
<p>Planting plans, patios and seasonal care in Leipzig.</p>
</section>
<div id="divider" aria-hidden="true"><span>↔</span></div>
</div>
<label for="comparison">After design visible</label>
<input id="comparison" type="range" min="0" max="100"
step="1" value="50" aria-describedby="instructions">
<output id="amount" for="comparison">50%</output>
<div class="presets">
<button type="button" data-value="0">Before</button>
<button type="button" data-value="50">Compare</button>
<button type="button" data-value="100">After</button>
</div>
<p id="instructions">Drag the range control or use its arrow keys.
Home shows before; End shows after.</p>
<p>Fictional concepts, not measured client results.</p>
</main>
Overlay both designs with CSS
The layers keep their full width. I clip the after layer instead of changing its width, so its text does not reflow as the slider moves. Both layouts still respond to the available preview width.
Place this CSS inside a style element in the document head, or in a stylesheet linked from your HTML. Include a viewport meta tag when testing on a phone.
* { box-sizing: border-box; }
body { margin: 0; background: #eef1e9; color: #18352e;
font: 16px/1.5 Arial, sans-serif; }
main { max-width: 860px; margin: auto; padding: 24px 16px; }
.preview { position: relative; height: 390px; overflow: hidden;
border: 1px solid #a8b9aa; border-radius: 12px; margin-bottom: 20px; }
.layer { position: absolute; inset: 0; padding: 28px; }
.layer h2 { max-width: 440px; font-size: clamp(28px, 5vw, 48px);
line-height: 1.1; }
.before { background: #faf5eb; color: #65594b; font-family: Georgia, serif; }
.after { background: #163e32; color: #f2f4d9;
clip-path: inset(0 0 0 50%); }
#divider { position: absolute; inset: 0 auto 0 50%;
width: 2px; background: #f6db50; pointer-events: none; }
#divider span { position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%); display: grid; place-items: center;
width: 44px; height: 44px; border: 3px solid white;
border-radius: 50%; background: #f6db50; color: #163e32; }
label { display: block; font-weight: bold; }
input[type="range"] { width: 100%; min-height: 48px; accent-color: #163e32; }
button { min-height: 44px; padding: 10px 16px; margin: 8px 4px 0 0;
border: 1px solid #8fa391; border-radius: 5px;
background: transparent; color: #18352e; cursor: pointer; }
button:active { background: #dce5d6; }
:focus-visible { outline: 3px solid #8245a9; outline-offset: 4px; }
@media (max-width: 480px) {
.layer { padding: 22px 18px; }
.preview { height: 430px; }
}
The four inset values represent top, right, bottom and left. At the initial position, inset(0 0 0 50%) hides the left half of the after layer. The before design remains visible there.
Connect the range input with JavaScript
Put this script after the HTML, before the closing body tag. The input event updates the preview while you move the range control.
const slider = document.querySelector("#comparison");
const after = document.querySelector("#after");
const divider = document.querySelector("#divider");
const amount = document.querySelector("#amount");
function updateComparison() {
const visible = Number(slider.value);
const split = 100 - visible;
after.style.clipPath = "inset(0 0 0 " + split + "%)";
divider.style.left = split + "%";
amount.textContent = visible + "%";
slider.setAttribute("aria-valuetext", visible + " percent after visible");
}
slider.addEventListener("input", updateComparison);
document.querySelectorAll("[data-value]").forEach((button) => {
button.addEventListener("click", () => {
slider.value = button.dataset.value;
updateComparison();
});
});
updateComparison();
The slider value describes how much of the after design is visible. At 0, the left inset is 100% and the after design disappears. At 100, the inset is 0 and the after design fills the preview. The divider follows the same boundary.
Keep keyboard and touch controls usable
I use the browser’s native range control rather than a draggable div. It provides built-in keyboard interaction, while the label names the control and aria-valuetext explains the percentage. Exact key behaviour should still be checked in your target browsers and assistive technology.
The preview layers are decorative in this example. A short accessible description explains the comparison. For a real case study, include the important differences as normal text outside the clipped preview so everyone can read them.
The control area is at least 48 pixels high. The buttons provide an alternative to precise dragging. I have not added transitions to the clipping boundary: the preview should follow the input without lag.
Check the component before using it in a project
- Use Before, Compare and After; verify the text does not change its wrapping while the boundary moves.
- Reach the slider with Tab and test the arrow keys, Home and End. Check that the focus outline remains visible.
- Test a narrow screen and browser zoom. Longer content may need more preview height; this fixed-height example is not a complete website viewer.
- Disable JavaScript. The two layers should still show the initial 50/50 comparison, although the controls will no longer update it.
If you replace the HTML concepts with screenshots, use matching dimensions and alignment. Keep decorative layers non-interactive: clipping a real link does not necessarily remove it from keyboard navigation.
Use the comparison to explain a design decision
In the demo, I changed the generic welcome into a concrete description of garden services and a clearer next step. That makes the intended difference easier to discuss. It does not prove that the revised design produces more enquiries.
I’m Marcus Lux, founder of Leipzig-Webdesign in Leipzig, Germany. I work on WordPress websites and SEO. I use small examples like this to make website decisions easier to inspect.
You can explore the full layout in the CodePen demo.
References
About the author
- I’m Marcus Lux, founder of Leipzig-Webdesign and a web designer and SEO consultant based in Leipzig, Germany. Since 2020, I have been building websites for independent professionals and businesses. My work combines website structure, clear content and WordPress development with local SEO. I also help businesses explore AI search visibility and practical uses of AI. Here I share technical notes, CSS examples and lessons from website projects. My first demo is a responsive before-and-after comparison built with HTML, CSS and vanilla JavaScript. Website: https://leipzig-webdesign.com/ CodePen: https://codepen.io/leipzigwebdesign
Comments
Join the discussionNo comments yet. Be the first to comment.