Slope chart
Before and after on two axes: lines draw in, rank moves sit beside each value, and switching datasets slides every line to its new slope.
pnpm dlx shadcn@latest add @uiarc/slope-chartConversion by channel
Conversion rate by channel, Americas, Q1 to Q2. Email 3.1% to 4.6%, Direct 3.9% to 3.7%, Organic search 3.4% to 3.6%, Referral 2.6% to 3.2%, Paid search 2.8% to 2.5%, Paid social 1.9% to 1.3%.
| Item | Q1 | Q2 | Change | Rank |
|---|---|---|---|---|
| 3.1% | 4.6% | +1.5 pts | 3 to 1 | |
| Direct | 3.9% | 3.7% | −0.2 pts | 1 to 2 |
| Organic search | 3.4% | 3.6% | +0.2 pts | 2 to 3 |
| Referral | 2.6% | 3.2% | +0.6 pts | 5 to 4 |
| Paid search | 2.8% | 2.5% | −0.3 pts | 4 to 5 |
| Paid social | 1.9% | 1.3% | −0.6 pts | 6 to 6 |
- Showing which items rose, which fell, and how the order changed between two periods.
- Replacing a grouped bar chart when the change is the story.
- Use line-chart for three or more moments.
- Use bar-chart when only the latest values matter.
- Avoid more than about ten items; labels start to crowd.
Installation
Add Slope chart with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/slope-chartAdds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:
import { SlopeChart } from "@/registry/components/slope-chart/slope-chart"; const channels = [ { key: "email", label: "Email", start: 3.1, end: 4.6 }, { key: "search", label: "Organic search", start: 3.4, end: 3.6 }, { key: "social", label: "Paid social", start: 1.9, end: 1.3 },]; export function Channels() { return <SlopeChart data={channels} label="Conversion by channel" startLabel="Q1" endLabel="Q2" highlightKey="email" formatValue={value => `${value.toFixed(1)}%`} />;}SlopeChart
Before and after for several items on two shared axes. Lines draw in on first view, labels spread apart to avoid overlaps with a hairline back to their point, rank moves sit beside each end value, and switching datasets slides every line to its new slope.
dataRequiredSlopeItem[]–Items: { key, label, start, end }. Keys keep lines stable across datasets.labelRequiredstring–What is measured. Names the chart, the summary, and the table.startLabelRequiredstring–First column heading, such as "Q1".endLabelRequiredstring–Second column heading, such as "Q2".formatValue(value: number) => string–Formats values beside the points, in the tooltip, and in the table.formatChange(change: number, item: SlopeItem) => string–Formats the change in the tooltip, such as "+1.5 pts".heightnumber44px per itemPlot height in pixels.highlightKeystring | nullnullThe item drawn in the first series hue, derived from the accent.activeKeystring | null–Controlled item in focus. The others fade.onActiveChange(key: string | null) => void–Called as the item in focus changes.ranksbooleantrueRank movement beside each end value.emptyLabelstring"No data"Message when there are no items.refRef<HTMLElement>–Forwarded to the figure.classNamestring–Extra class on the figure.- Tab
- Focuses the chart and reads the top item by end value.
- ArrowDownorArrowRight
- Next item in end order.
- ArrowUporArrowLeft
- Previous item.
- HomeorEnd
- First or last item.
- Escape
- Clears the reading.
- The chart is a focusable group with a roledescription and instructions; lines and labels are aria-hidden.
- A polite live region reads both values, the change, and the rank move for the item in focus.
- A visually hidden summary and a table list every item in end order.
- Rank moves use an arrow glyph and a number, never colour alone.
- On first view each line draws from start to end with a small stagger, and the end dots and labels arrive as it lands.
- Switching datasets moves lines, dots, labels, and leader hairlines on the shared morph spring; rank numbers roll in the direction they moved.
- The item in focus comes forward while the others fade; the tooltip glides to the middle of its line.
- Reduced motion shows every line drawn and moves it immediately.
- Label columns narrow below 440px and names truncate with an ellipsis; the full name stays in the tooltip and table.
- Overlapping labels spread apart with a hairline to their point instead of colliding.
- Touch reads the nearest line with a press and drag.
- SVG lines and HTML labels animated with springs; around ten items render in a few dozen nodes.
- One ResizeObserver measures the container.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Choose it for exactly two moments and three to ten items: before and after a launch, quarter over quarter, last year against this year.
- Use highlightKey for the item the story is about.
- Give the columns short headings; names sit on the left only, values on both sides.
- For more than two moments use line-chart.
The full library index for assistants is at /llms.txt.