Skip to main content

ParallelCoordinatesChart

Comparisonchart

Same entities across 3-6 mixed-unit dimensions: one hairline per entity threaded through per-axis-normalized vertical scales, so shape and crossing pattern reveal correlation across axes that share no common unit.

import { ParallelCoordinatesChart } from "@elabs-ai/components-charts";
Install
npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.

Add the brand-ui ParallelCoordinatesChart chart (from @elabs-ai/components-charts) to this project.

1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`.
2. Look ParallelCoordinatesChart up before writing code: `npx -y @elabs-ai/components-cli docs ParallelCoordinatesChart` prints its real props, variants, what it pairs with and what to avoid.
3. If @elabs-ai/components-charts is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`.
4. Use it here: [the screen or file, and what it should show]
5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop.
6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.

Use it for

  • How do these compare? Many entities, many dimensions.
  • many numeric dimensions compared across entities at once

Avoid

  • more than about 2 entities need per-entity detail — use small-multiple radar
  • Fewer than 3 or more than 6 dimensions — below 3 a DumbbellChart (2) or MetricCard (1) reads better; above 6 the axes crowd and `resolveParallelDimensions` clamps to the first 6 with a console warning, silently dropping the rest.
  • Dimensions on wildly different natural scales with no explicit `domain` — each axis normalizes independently, so an unset domain just uses that dimension's own min/max, which is usually right; only override when you need a domain that isn't in the data (e.g. a known 0-100 ceiling).
  • Using `highlightKey` to try to promote MULTIPLE entities to hero status — only the first row a predicate matches (or the one literal id) is promoted; for comparing several entities at once, dim the rest via a different technique or fall back to small multiples.
  • Reaching for this when the entities do NOT share the same dimensions, or when there is only one entity — a single-entity profile across many metrics is a RadarChart (shared 0-100 radial scale) or a MetricGrid, not a parallel-coordinates plot.

Works with

Contains

Examples

8 live examples

Default

Twelve products across four mixed-unit dimensions — the L20 acceptance recreation. No hero: every hairline draws at its seeded 0.5–0.8 opacity.

Loading

`status="loading"` (RM-184): the skeleton + `ChartLoadingLabel`, until the data arrives.

Hero Line

`highlightKey` as a literal entity id promotes ONE line to the 2px hero — drawn last (on top) with a halo label at its final axis point.

Hero By Predicate

`highlightKey` as a predicate promotes the first row it matches — here, the first product clearing an NPS of 50.

Show Extremes

`showExtremes` draws each axis's min/max value at its foot.

Monotone Curve

`curve="monotone"` draws a smoothed `curveMonotoneX` bézier between axes instead of a straight hairline segment.

Categorical Few Entities

Three axes (the minimum) with an explicit `categorical` palette, honoured up to the six-entity soft cap.

Keyboard Cycling

Setting `onDatapointClick` mounts the keyboard/pointer interaction layer — arrow keys cycle through every entity, Enter activates. Verifies the roadmap's "keyboard cycles through entities" acceptance bullet.

API

ParallelCoordinatesChart

PropTypeDescription
messagesChartMessagesmessages group (RM-187): this chart's own words, keyed by the ui catalogue's `charts.*` message keys. A key set here wins over the `LocaleProvider`; every other key reads the catalogue as before.
datarequiredRecord<string, unknown>[]Data array — one row per entity.
entityrequiredstringKey in `data` for the entity label (drawn in the tooltip title and the hero halo label).
dimensionsrequiredParallelCoordinatesDimension[]3–6 axes, left to right. See {@link resolveParallelDimensions} for what happens outside that range.
highlightKeystring | number | ((datum: Record<string, unknown>) => boolean)Names the ONE entity promoted to the hero line — a string or number matched against `entity`'s column (`==`-free, via `String(…)`, the same rule `Bar`'s `highlightKey` uses), or a predicate over the row datum. `undefined` (default): no hero, every line draws at the same seeded hairline opacity.
curveParallelCoordinatesCurve`"linear"` (default) or `"monotone"` (curveMonotoneX) between axes.
showExtremesbooleanDraw each axis's min/max value at its foot. Default `false`.
paletteChartPaletteWhich colour family entity lines draw from. Default `"mono"` — many hairlines read as a texture, not a legend, so `"categorical"` is honoured only up to `resolvePalette`'s own six-entity soft cap (past that it degrades to `"mono"` and warns, exactly like every other container — see `chart-context.tsx`).
marginPartial<Margin>Chart margins.
aspectRatiostringAspect ratio as `"width / height"`. Default `"2 / 1"`, and `"1.25 / 1"` at the narrow tier (`DEFAULT_CHART_PLOT_HEIGHT`).
plotHeightResponsive<ChartPlotHeight>The plot's own height (ADR 0039): px, or `{ aspect }` (width ÷ height), optionally per breakpoint. Wins over `aspectRatio`, which stays an alias.
classNamestring
accessibleLabelChartA11yProps["accessibleLabel"]Accessible name for the chart region (announces to AT on focus).
accessibleDescriptionChartA11yProps["accessibleDescription"]Supplemental description read by AT (e.g. entity count + dimension list).
statusChartStatusShow the loading skeleton until the data is ready (ADR 0042 §9). Default `"ready"`.
emptyChartEmptyStateTitle and message shown when there is nothing to plot.
onDatapointClickChartDatapointClickHandler<Record<string, unknown>>Fires when a datapoint is activated by pointer OR keyboard. Setting it is what enables the interaction layer — with it unset the chart renders exactly as before, with no extra DOM and no new focusables.
copyValueOnActivatebooleanPut the datapoint's EXACT value on the clipboard when it is activated — the recovery path for compact axis labels and tooltips, reachable by mouse AND keyboard because activation rides `ChartDatapointLayer`'s real buttons. Default `false` on a raw chart container: turning it on mounts the interaction layer, and the documented guarantee is that a chart with no interaction props renders byte-identical DOM. `AutoChart` — the spec-driven surface, where the caller never sees the axis config — turns it on by default. A consumer-supplied `onDatapointClick` always wins; the two never both run.
datapointLabelChartDatapointLabel<Record<string, unknown>>Override the accessible name of each keyboard target.
maxInteractiveDatapointsnumberDev-warning threshold on the number of keyboard targets. Default 500. NOT a functional cap — every plotted point stays reachable, so a keyboard user can reach exactly what a mouse user can click.

Also accepts: ChartInteractionProps

HaloText

PropTypeDescription
halostringThe halo colour. Defaults to `var(--chart-background)` — the plot ground — which is what makes the mark THEME-SAFE: on a light card the halo is light, on a dark card it is dark, with no `dark:` override and no literal anywhere. Override it only when the text sits on something other than the plot ground (a tooltip plate, a filled zone) — and then with another semantic token. The halo tracks the PLOT ground, not the card. The shipped themes declare that mirror in the token layer (`--chart-background: var(--card)` in `themes.css`), so the two agree by contract, not by coincidence. A theme that paints the plot on something other than `--card` must set `--chart-background` to that ground, or every halo punches a card-coloured hole in it.
haloWidthnumberHalo width in px (default 3). The visible halo is half of it — a stroke is centred.

Also accepts: SVGProps<SVGTextElement>

DrawPath

PropTypeDescription
durationnumberDraw duration in seconds (default 0.9).
delaynumberDelay before drawing starts, in seconds — pair it with `stagger(i, …)`.
transitionTransitionFull transition override. Wins over `duration`/`delay`.

Also accepts: Omit< SVGProps<SVGPathElement>, | "onAnimationStart" | "onAnimationEnd" | "onAnimationIteration" | "onDrag" | "onDragEnd" | "onDragEnter" | "onDragExit" | "onDragLeave" | "onDragOver" | "onDragStart" | "onDrop" | "pathLength" | "strokeDasharray" | "strokeDashoffset" | "style" | "values" >

ChartDatapointLayer

PropTypeDescription
labelstringAccessible name for the target group. Defaults to a localized generic name.

Also accepts: HTMLAttributes<HTMLDivElement>

Theming

The tokens each state resolves to. Change the token and every theme follows.

StateToken
axisvar(--chart-foreground-muted) vertical hairline with end ticks (HairlineFloor style)
extremeLabelvar(--chart-foreground-muted) on a var(--chart-background) halo, axis feet only when showExtremes
heroLine2px, var(--chart-foreground) or the resolved palette ink, opacity 1 — drawn LAST so it sits on top
nonHeroLine0.65px hairline, seeded 0.5-0.8 opacity — a texture of many lines, not individually legible