CompareSlider
5 partsA 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
Anatomy
Import the parts and compose them. Each part forwards attributes to its underlying element.
import {
CompareSliderRoot,
CompareSliderAfter,
CompareSliderBefore,
CompareSliderDivider,
CompareSliderHandle,
} from '@robonen/primitives/canvas/compare-slider';
<CompareSliderRoot>
<CompareSliderAfter />
<CompareSliderBefore />
<CompareSliderDivider />
<CompareSliderHandle />
</CompareSliderRoot>API Reference
CompareSliderRoot
RootA 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.
| Prop | Type | Description |
|---|---|---|
orientation? | CompareSliderOrientation | Orientation. |
defaultPosition? | number | Uncontrolled initial reveal position (0–100). |
disabled? | boolean | Disable all interaction. |
inverted? | boolean | Invert the direction of interaction (and the revealed side). |
dir? | CompareSliderDirection | Writing direction. When omitted it is inherited from the nearest
ConfigProvider (falling back to 'ltr'); an explicit value wins. |
keyboardStep? | number | Position change per Arrow key press. |
keyboardLargeStep? | number | Position change per Shift+Arrow / Page key press. |
hover? | boolean | When true the divider follows the pointer on hover, without a press. |
valueText? | CompareSliderValueText | Optional formatter producing a human-friendly aria-valuetext for the
handle. Receives the reveal position (0–100). |
position? | number | Two-way bound value (v-model:position). |
| Event | Payload |
|---|---|
update:position | [value: number] |
CompareSliderAfter
AfterThe 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
BeforeThe 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
DividerA 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
HandleThe 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.
| Prop | Type | Description |
|---|---|---|
valueText? | (position: number) => string | undefined | Optional formatter producing this handle's aria-valuetext. Overrides the
root-level valueText for this handle when provided. Receives the reveal
position (0–100). |