a1ui
6 new components shipped

Compare Curtain

A draggable divider that reveals two aligned images or design states.

Read agent guide

Demo

Drag the center arrow to compare50%
Draft / 01a new perspective.Working concept
Published / 02a new perspective.Final design
PublishedDraft

Drag the arrow with a mouse or touch. Focus it and use arrow keys to move one percent, Shift with an arrow key to move ten percent, or Home and End to jump to either side.

Installation

pnpm dlx shadcn@latest add https://ui.arjayby1.xyz/r/compare-curtain.json

Code

"use client";

import { useState } from "react";

import { CompareCurtain } from "@/components/ui/compare-curtain";

export function ImageComparison() {
  const [reveal, setReveal] = useState(50);

  return (
    <CompareCurtain
      before={<div className="grid size-full place-items-center bg-background">Draft design</div>}
      after={<div className="grid size-full place-items-center bg-foreground text-background">Finished design</div>}
      beforeLabel="Draft"
      afterLabel="Finished"
      ariaLabel="Reveal finished design"
      value={reveal}
      onValueChange={setReveal}
    />
  );
}

API reference

PropTypeDefaultPurpose
beforeReactNodeRequiredFull layer behind the reveal.
afterReactNodeRequiredLayer revealed from the left.
beforeLabelstringBeforeLabel beneath the right side.
afterLabelstringAfterLabel beneath the left side and part of the spoken value.
ariaLabelstringReveal after imageAccessible name for the draggable divider.
valuenumberNoneControlled reveal percentage. Pair with onValueChange.
defaultValuenumber50Initial percentage in uncontrolled mode.
onValueChange(value: number) => voidNoneRuns when the divider changes.
disabledbooleanfalseStops pointer and keyboard changes.
classNamestringNoneOuter layout classes.
styleCSSPropertiesNoneInline styles on the outer element.

Drag the center arrow directly with a mouse, pen, or touch. The divider is the only control. It clamps to 0–100 percent, keeps its arrow visible at the ends, and has no automatic animation.

The arrow has slider semantics. Focus it and use arrow keys for one percent, Shift with arrow keys or Page Up and Page Down for ten percent, and Home or End for the boundaries. Provide a clear ariaLabel and useful alternative text for any images in both layers.

Both layers fill the same frame. Use images or other content that can be cropped without changing their alignment. The component uses the surrounding theme's foreground, background, and muted colors.