CompareSlider

5 parts

A before/after split-reveal slider: two stacked layers (a base CompareSliderBefore and a clipped CompareSliderAfter) with a draggable divider that reveals exactly position% of the after-layer. The root owns the reveal position (controlled via v-model:position or uncontrolled via defaultPosition), clamps it to 0–100, and starts a pointer drag on press — mapping the pointer's position over the root's box onto the reveal percentage. When hover is set the divider follows the pointer on hover (no press needed). It provides context to CompareSliderBefore, CompareSliderAfter, CompareSliderHandle, and CompareSliderDivider, and supports horizontal/vertical orientation plus dir/inverted direction. Reach for it to compare two images, designs, or any two overlaid views.

Demo

Loading demo…

Anatomy

Import the parts and compose them. Each part forwards attributes to its underlying element.

vue
import {
  CompareSliderRoot,
  CompareSliderAfter,
  CompareSliderBefore,
  CompareSliderDivider,
  CompareSliderHandle,
} from '@robonen/primitives/canvas/compare-slider';

<CompareSliderRoot>
  <CompareSliderAfter />
  <CompareSliderBefore />
  <CompareSliderDivider />
  <CompareSliderHandle />
</CompareSliderRoot>

API Reference

CompareSliderRoot

Root

A before/after split-reveal slider: two stacked layers (a base `CompareSliderBefore` and a clipped `CompareSliderAfter`) with a draggable divider that reveals exactly `position`% of the after-layer. The root owns the reveal position (controlled via `v-model:position` or uncontrolled via `defaultPosition`), clamps it to 0–100, and starts a pointer drag on press — mapping the pointer's position over the root's box onto the reveal percentage. When `hover` is set the divider follows the pointer on hover (no press needed). It provides context to `CompareSliderBefore`, `CompareSliderAfter`, `CompareSliderHandle`, and `CompareSliderDivider`, and supports horizontal/vertical `orientation` plus `dir`/`inverted` direction. Reach for it to compare two images, designs, or any two overlaid views.

PropTypeDescription
orientation?CompareSliderOrientationOrientation.
defaultPosition?numberUncontrolled initial reveal position (0–100).
disabled?booleanDisable all interaction.
inverted?booleanInvert the direction of interaction (and the revealed side).
dir?CompareSliderDirectionWriting direction. When omitted it is inherited from the nearest ConfigProvider (falling back to 'ltr'); an explicit value wins.
keyboardStep?numberPosition change per Arrow key press.
keyboardLargeStep?numberPosition change per Shift+Arrow / Page key press.
hover?booleanWhen true the divider follows the pointer on hover, without a press.
valueText?CompareSliderValueTextOptional formatter producing a human-friendly aria-valuetext for the handle. Receives the reveal position (0–100).
position?numberTwo-way bound value (v-model:position).
Emits
EventPayload
update:position[value: number]

CompareSliderAfter

After

The revealed ("after") layer, drawn on top of `CompareSliderBefore` and clipped via `clip-path: inset(...)` so that exactly `position`% of it is shown. The clip side is driven by the root's combined `flip` flag (so the revealed region always sits on the same side as the divider). At `0` / `100` the layer is fully hidden / shown with no sub-pixel sliver. Rendered inside `CompareSliderRoot` and absolutely positioned to fill it (`inset: 0`); put the comparison image / view here.

No props or events — renders its element and forwards attributes.

CompareSliderBefore

Before

The base ("before") layer of the comparison — the full, unclipped content that sits underneath. Rendered inside `CompareSliderRoot` and absolutely positioned to fill it (`inset: 0`). Put the original image / view here; the `CompareSliderAfter` layer is drawn on top and clipped to reveal it.

No props or events — renders its element and forwards attributes.

CompareSliderDivider

Divider

A thin presentational line marking the split between the before and after layers, positioned at the current reveal position. It is purely decorative (no role, no keyboard) — the focusable, screen-reader-accessible target is `CompareSliderHandle`. The recommended two-element pattern is a thin `CompareSliderDivider` line for the visible seam plus a larger `CompareSliderHandle` "puck" for the grab / focus target sitting on top of it (give the handle a wider hit-area via CSS). Render the divider as a sibling or wrapper of the handle inside the root.

No props or events — renders its element and forwards attributes.

CompareSliderHandle

Handle

The keyboard- and screen-reader-accessible divider handle, rendered as `role="slider"` with full ARIA value attributes (`aria-valuemin=0`, `aria-valuemax=100`, `aria-valuenow=position`). It positions itself at the divider and handles keyboard interaction: Arrow keys move the divider toward the after/before layer by `keyboardStep` (orientation- and direction-aware), Shift+Arrow and Page keys by `keyboardLargeStep`, and Home/End jump to 0/100. This is the hit-target / focus element; pair it with a thin presentational `CompareSliderDivider` for the visible line. Exposes `position` as a slot prop.

PropTypeDescription
valueText?(position: number) => string | undefinedOptional formatter producing this handle's aria-valuetext. Overrides the root-level valueText for this handle when provided. Receives the reveal position (0–100).