Block Comparison — React Page Block
Before/after comparison with a draggable reveal handle — keyboard (arrow keys, Home/End), touch and screen readers all work because the handle is a real range input.

Usage
import { BlockComparison } from "@/components/BlockComparison";
export function BeforeAfter() {
return (
<BlockComparison
title="Drag to see the redesign."
before={<img src="/before.png" alt="Before the redesign" />}
after={<img src="/after.png" alt="After the redesign" />}
beforeLabel="Old site"
afterLabel="Our work"
{...{
title: "A new look. The same good coffee.",
defaultPosition: 50,
accent: "#d5ed9a",
scheme: "dark"
}}
/>
);
}Implementation notes & API
Purpose: before/after comparison slider. Copy it, ship it — every string, link and color arrives through props. Runtime: pure DOM + CSS. No WebGPU, canvas, animation library or external assets; SSR-safe on first render. Theming: root custom properties (--vb-bg, --vb-fg, --vb-accent, --vb-border, --vb-radius, --vb-container) or the accent/scheme/className/style props. scheme flips the dark default to light. Guardrails: responsive via container queries down to 320px; interactive behavior degrades on touch; prefers-reduced-motion drops entrance/scroll motion while content stays complete. The overlay range input owns interaction (drag, touch, keyboard, aria-valuetext); the visible handle is pointer-events:none decoration.
| Property | Type | Notes / default |
|---|---|---|
eyebrow / title / description | ReactNode | Section header |
before / after | ReactNode | The two layers; defaults are CSS demo panels |
beforeLabel / afterLabel | string | Chip labels (and the slider's accessible name) |
defaultPosition | number | 0-100 handle start; user moves are uncontrolled |
caption | ReactNode | Note under the stage |
scheme | "dark" | "light" | dark |
accent | string (hex) | Sets --vb-accent for the whole block |
className / style | string / CSSProperties | Applied to the section root |
children | ReactNode | Replaces the default content entirely |