Interactive Answers
Mirror Strips
Rows of dot strips with two series mirrored around a line — compare rounds or weeks on one axis, with pinned columns and notes.
2 variantsMIT
Preview
Edits by page
42 pages · 4 rounds
↑ Their edits↓ Our redlines
1 dot = 2
Installation
Copies the component into components/ui and installs its dependencies and theme tokens.
pnpm dlx shadcn@latest add https://tuffui.com/r/mirror-strips.jsonUsage
Pass your own data and wire the callbacks to your agent runtime.
import { MirrorStrips } from "@/components/ui/mirror-strips";
// A contract review: where each side edited, round by round.
export function RedlineRounds({ review }: { review: Review }) {
return (
<MirrorStrips
title="Edits by page"
subtitle={`${review.pages} pages · ${review.rounds.length} rounds`}
columns={review.pageLabels}
rows={review.rounds.map((r, i) => ({
id: r.id,
label: r.name,
sublabel: r.date,
up: r.theirEdits,
down: r.ourRedlines,
current: i === review.rounds.length - 1,
}))}
series={{ up: { label: "Their edits" }, down: { label: "Our redlines" } }}
marks={review.openClauses.map((c) => ({ column: c.page, label: c.ref, note: c.title }))}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| columns* | string[] | — | One label per column. Every few are drawn under the chart. |
| rows* | MirrorRow[] | — | Each row has one count per column above the line and one below. |
| series* | { up: { label; color? }; down: { label; color? } } | — | Names and colors for the two sides. Defaults to foreground above, brand below. |
| title* | string | — | Header title. |
| subtitle | string | — | Secondary line under the title. |
| marks | MirrorMark[] | — | Columns pinned with a label above the chart and a note in the tooltip. |
| onSelect | (column: number) => void | — | Called when a column is hovered or reached with the arrow keys. |
| className | string | — | Extra classes for the root element. |
type MirrorRow = {
id: string;
label: string;
sublabel?: string;
up: number[];
down: number[];
current?: boolean; // emphasized label and total
};
type MirrorMark = { column: number; label: string; note?: string };
// Stacks hold up to five dots each way; bigger counts switch to one dot per group.