# Compare slider

Two versions of the same thing side by side, with a divider the person drags to see more of one or the other: what a checkpoint restore would put back against what is there now, a prose rewrite against the original, a dashboard before and after a change. The `before` side is at the start of the line (the left in left-to-right text) and `after` at the end; both are drawn at the full width and the divider crops the `before` one, so the same point lines up on both.

The divider is a real slider: Left and Right arrows move it 1%, with Shift (or Page Up and Page Down) 10%, and Home and End show all of the `after` side or all of the `before` side. The value is read aloud as the two labels and how much of the width each one has. Both labels are always printed, and the app says in `caption` what is being compared and where each side came from. Nothing animates except the divider following the pointer or the keys.

Both sides stay in the page for assistive tech while any of them shows, so a screen reader can read each in full. A side the divider has cropped away entirely (Home or End, or `split` at 0 or 100) is `inert` and `aria-hidden`, so keyboard focus can't land on a control nobody can see; both labels stay printed and are still read. Treat the contents as something to look at: a control inside a partly cropped half can't be seen or reached by pointer. `split` makes it controlled; leave it out and `defaultSplit` is the start.

- Registry name: `compare-slider` (`@candor/compare-slider`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/compare-slider

## Install

```bash
npx candor-ui add compare-slider
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/compare-slider`.
It brings in: locale, tokens, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { CompareSlider } from "@/components/compare-slider"

/*
 * A checkpoint restore preview: what the file says now against what it said at the checkpoint. The app supplies both
 * sides and says in the caption where they were read from; the slider only lines them up.
 */
export default function Example() {
  return (
    <CompareSlider
      className="max-w-xl"
      before={{
        label: "As it is now",
        content: (
          <div className="flex flex-col gap-1 text-sm">
            <p className="font-medium">Acme renewal: notes</p>
            <p>Renewal date moved to 1 March.</p>
            <p>Keel sent the revised quote to Dana Whitfield on Tuesday.</p>
            <p>Open question: does Acme still want the 3-year term?</p>
          </div>
        ),
      }}
      after={{
        label: "After restore",
        content: (
          <div className="flex flex-col gap-1 text-sm">
            <p className="font-medium">Acme renewal: notes</p>
            <p>Renewal date is 1 February.</p>
            <p>Quote not yet sent.</p>
            <p>Open question: does Acme still want the 3-year term?</p>
          </div>
        ),
      }}
      caption="Notes.md in the Acme workspace now, against its copy in checkpoint 3 (Tuesday 09:12), read from Keel’s workspace."
    />
  )
}
```

## <CompareSlider>

Props:

- `before: ComparePane`: The side at the start of the line: the older, or "as it is now".
- `after: ComparePane`: The side at the end of the line: the newer, or "after restore".
- `caption?: React.ReactNode | undefined`: What is being compared and where the data came from: "Notes.md at checkpoint 3 against today, read from the build agent's workspace." Printed under the comparison.
- `split?: number | undefined`: How much of the width the `before` side shows, 0 to 100. Makes the slider controlled: the divider is where this says, and moves only when the app changes it.
- `defaultSplit?: number | undefined` (default `50`): Where the divider starts when `split` isn't given: 50, the middle, favouring neither side.
- `onSplitChange?: ((split: number) => void) | undefined`: Called with the new position (0 to 100) as the person drags or presses a key.
- `className?: string | undefined`

## Strings (`compareSlider`)

The fixed words, English by default. Override any of them under `compareSlider` in `<LocaleProvider strings={{ compareSlider: { … } }}>`; the table is exported as `compareSliderStrings`.

- `divider`: Divider between the two sides
- `valueText`: {before} {split}%, {after} {rest}%

## Types

### ComparePane

One side of a CompareSlider.

```ts
{
  /** Always shown above its side, for as long as the comparison is on screen: "As it is now", "After restore". Neither label is styled as the better one. */
  label: string;
  /** What this side shows, as React nodes. It is drawn at the full width of the comparison and cropped by the divider, never scaled, so the two sides line up. */
  content: React.ReactNode;
}
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
