Compare
Side-by-side comparison slider component.
View SourceInstallation
Follow instructions for individual framework usage below
Styles
Variants
Scripts
The Compare script updates the comparison position and adjusts the range step for keyboard and pointer interaction.
Examples
Default
<x-compare class="x-compare rounded size-fit accent-light">
<input type="range" step="0.1" aria-hidden="true" data-x-compare-part="input">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<img src="https://placehold.co/896x504/333/webp" alt="Image 1" loading="lazy" width="896" height="504">
<img src="https://placehold.co/896x504/888/webp" alt="Image 2" loading="lazy" width="896" height="504">
</div>
</x-compare><div data-controller="x-compare" class="x-compare rounded size-fit accent-light">
<input type="range" step="0.1" aria-hidden="true" data-action="input->x-compare#setPosition keydown->x-compare#setKeyboardStep mousedown->x-compare#setMouseStep">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<img src="https://placehold.co/896x504/333/webp" alt="Image 1" loading="lazy" width="896" height="504">
<img src="https://placehold.co/896x504/888/webp" alt="Image 2" loading="lazy" width="896" height="504">
</div>
</div><script setup lang="ts">
import { Compare } from '@/components/compare'
</script>
<template>
<Compare class="rounded size-fit accent-light">
<input type="range" step="0.1" aria-hidden="true">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<img src="https://placehold.co/896x504/333/webp" alt="Image 1" loading="lazy" width="896" height="504">
<img src="https://placehold.co/896x504/888/webp" alt="Image 2" loading="lazy" width="896" height="504">
</div>
</Compare>
</template>import { Compare } from '@/components/compare'
export function Example() {
return (
<Compare className="rounded size-fit accent-light">
<input type="range" step="0.1" aria-hidden="true" />
<div className="x-button circle" aria-hidden="true">
<svg className="size-6" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" fill="none">
<path className="-translate-x-1.5 scale-75 origin-center" strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path className="translate-x-1.5 scale-75 origin-center" strokeLinecap="round" strokeLinejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div className="x-image">
<img src="https://placehold.co/896x504/333/webp" alt="Image 1" loading="lazy" width="896" height="504" />
<img src="https://placehold.co/896x504/888/webp" alt="Image 2" loading="lazy" width="896" height="504" />
</div>
</Compare>
)
}Text
<x-compare class="x-compare rounded w-fit accent-dark">
<input type="range" step="0.1" aria-hidden="true" data-x-compare-part="input">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<div class="bg-primary text-light text-9xl font-black grid place-content-center p-6">PRIMARY</div>
<div class="bg-main text-body text-9xl font-black grid place-content-center p-6">PRIMARY</div>
</div>
</x-compare><div data-controller="x-compare" class="x-compare rounded w-fit accent-dark">
<input type="range" step="0.1" aria-hidden="true" data-action="input->x-compare#setPosition keydown->x-compare#setKeyboardStep mousedown->x-compare#setMouseStep">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<div class="bg-primary text-light text-9xl font-black grid place-content-center p-6">PRIMARY</div>
<div class="bg-main text-body text-9xl font-black grid place-content-center p-6">PRIMARY</div>
</div>
</div><script setup lang="ts">
import { Compare } from '@/components/compare'
</script>
<template>
<Compare class="rounded w-fit accent-dark">
<input type="range" step="0.1" aria-hidden="true">
<div class="x-button circle" aria-hidden="true">
<svg class="size-6" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none">
<path class="-translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path class="translate-x-1.5 scale-75 origin-center" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div class="x-image">
<div class="bg-primary text-light text-9xl font-black grid place-content-center p-6">PRIMARY</div>
<div class="bg-main text-body text-9xl font-black grid place-content-center p-6">PRIMARY</div>
</div>
</Compare>
</template>import { Compare } from '@/components/compare'
export function Example() {
return (
<Compare className="rounded w-fit accent-dark">
<input type="range" step="0.1" aria-hidden="true" />
<div className="x-button circle" aria-hidden="true">
<svg className="size-6" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" fill="none">
<path className="-translate-x-1.5 scale-75 origin-center" strokeLinecap="round" strokeLinejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
<path className="translate-x-1.5 scale-75 origin-center" strokeLinecap="round" strokeLinejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</div>
<div className="x-image">
<div className="bg-primary text-light text-9xl font-black grid place-content-center p-6">PRIMARY</div>
<div className="bg-main text-body text-9xl font-black grid place-content-center p-6">PRIMARY</div>
</div>
</Compare>
)
}JavaScript API
Low-level helpers used by winduum-elements and winduum-stimulus — you can use them to wire the component manually or build your own integration.
All three helpers work with the <input type="range"> element inside .x-compare — attach them to its events:
import { setPosition, setKeyboardStep, setMouseStep } from 'winduum/src/components/compare'
document.querySelectorAll('.x-compare input[type="range"]').forEach((inputElement) => {
inputElement.addEventListener('input', (event) => setPosition(event.target))
inputElement.addEventListener('keydown', (event) => setKeyboardStep(event.target, event.key))
inputElement.addEventListener('mousedown', (event) => setMouseStep(event.target))
})setPosition
- Type:
(element: HTMLInputElement, options?: SetPositionOptions) => void - Kind:
sync
Sets the --x-compare-position CSS property on the closest .x-compare element to the current input value. Call it on the input event.
inputElement.addEventListener('input', (event) => setPosition(event.target))SetPositionOptions
selector
- Type:
string - Default:
.x-compare
positionProperty
- Type:
string - Default:
--x-compare-position
setKeyboardStep
- Type:
(element: HTMLInputElement, key: string, step?: string) => void - Kind:
sync - Default step:
'10'
Sets a larger input step when ArrowLeft or ArrowRight is pressed, so keyboard users move the slider in bigger increments. Call it on the keydown event.
inputElement.addEventListener('keydown', (event) => setKeyboardStep(event.target, event.key))setMouseStep
- Type:
(element: HTMLInputElement, step?: string) => void - Kind:
sync - Default step:
'0.1'
Restores a fine input step for smooth mouse dragging. Call it on the mousedown event.
inputElement.addEventListener('mousedown', (event) => setMouseStep(event.target))