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.
import { SlopeChart } from "@sagui/ui";When to use
- 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.
When not to use
- 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
Install the package and import the styles once. The installation guide covers the Tailwind setup.
$ npm install @sagui/uiUsage
import { SlopeChart } from "@sagui/ui";
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)}%`} />;
}Examples
Switching datasets
Lines keep their key, so a new dataset slides every line to its new slope.
API reference
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.
| Prop | Type | Default | Description |
|---|---|---|---|
data (required) |
SlopeItem[] |
– | Items: { key, label, start, end }. Keys keep lines stable across datasets. |
label (required) |
string |
– | What is measured. Names the chart, the summary, and the table. |
startLabel (required) |
string |
– | First column heading, such as "Q1". |
endLabel (required) |
string |
– | 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". |
height |
number |
44px per item |
Plot height in pixels. |
highlightKey |
string | null |
null |
The item drawn in the first chart color (--color-chart-1). |
activeKey |
string | null |
– | Controlled item in focus. The others fade. |
onActiveChange |
(key: string | null) => void |
– | Called as the item in focus changes. |
ranks |
boolean |
true |
Rank movement beside each end value. |
emptyLabel |
string |
"No data" |
Message when there are no items. |
ref |
Ref<HTMLElement> |
– | Forwarded to the figure. |
className |
string |
– | Extra class on the figure. |
Keyboard interactions
| Keys | Action |
|---|---|
| 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. |
Accessibility
- 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.
Motion
- 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.
Responsive behavior
- 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.
Performance
- SVG lines and HTML labels animated with springs; around ten items render in a few dozen nodes.
- One ResizeObserver measures the container.
Notes
- 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.