Skip to main content

TreemapChart

Part to Wholechart

Two-level squarified treemap — a hierarchy where area, not position, encodes value.

Four product families, two modules each, sized by ARR.
import { TreemapChart } 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 TreemapChart 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 TreemapChart up before writing code: `npx -y @elabs-ai/components-cli docs TreemapChart` 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

  • What is it made of? Hierarchy and share.
  • a nested hierarchy sized by one measure

Avoid

  • the hierarchy has fewer than 2 levels — a flat bar chart is clearer
  • Comparing a leaf's area against a leaf in a different group — area is only comparable WITHIN one parent's total; two leaves under different groups are fractions of different denominators, so their tile sizes are not commensurate across the group boundary.
  • Reaching for palette: 'categorical' to encode a second measure on top of area — the hue there still only labels the group a leaf already belongs to (structurally, via the band); asking it to carry an unrelated variable makes one channel do two jobs and defeats the mono default's greyscale-legible design.
  • Leaving a long tail of small leaves unmerged until tiles fall below labelMinArea — set otherThreshold before that happens (the unconditional 30-leaf cap is a backstop, not a substitute for tuning it); a treemap of unlabeled slivers reads as noise, not a breakdown.
  • Turning on drilldown as the only way to reach a leaf's value — the group-band zoom button is a SEPARATE affordance from onDatapointClick on purpose; a leaf stays selectable by its own click/Enter without first forcing a reader to drill in.

Works with

Contains

  • HaloText
  • ChartTooltipBox
  • ChartDatapointLayer

Pairs with

Examples

14 live examples

Default

Loading

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

Mono

Default palette: one shared neutral shade per leaf — groups read apart only by their title band + paper gap, so the chart stays legible in greyscale.

Categorical

One hue per top-level group (≤ 4 groups); leaves inherit their group's hue.

Sequential

Leaf shade encodes the leaf's own value on the sequential ramp.

Legend, categorical groups

One row per top-level group, colour-keyed to the same hue each group's leaves/title band paint. Hovering a row dims every OTHER group's tiles.

Legend, sequential ramp

`palette="sequential"` has no discrete key — it renders the shared `RampLegend` instead.

Depth: 1 (flat)

Flat, single-level rendering — root's children only, no title bands.

Other Threshold

A long tail of small leaves merges into a single "Other" tile once each falls below 5% of its group's total.

Show Values

`labels` (#247) prints each tile's value under its name, so the quantity survives a screenshot, an export and a keyboard user — not only a hover. A tile too short or narrow for the whole number keeps its name and drops the value; values share one notation across the set. Off by default.

Show Values Other Threshold

The folded "Other" tile with its value printed — how much the long tail adds up to.

Drilldown

Click (or Tab to and press Enter on) a group's title band to zoom into it; the "← Work" control zooms back out. This is a SEPARATE affordance from `onDatapointClick` — the static stories above have no click handler beyond it.

Interactive

Click a leaf — or Tab in and press Enter — to report its datum below.

Selection states

`selectionStates` paints the host’s tri-state on leaf tiles: selected marks carry a compound foreground/background outline, excluded marks dim AND carry a full-opacity dashed frame, so the three states stay distinguishable in greyscale.

API

TreemapChart

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.
datarequiredTreemapNodeThe hierarchy. A leaf needs a `value`; a parent's explicit `value` (if any) must equal the sum of its children (dev-validated — see `validateTreemapData`).
depth1 | 2How many levels of rectangles to render. `1` = flat (root's children only, no title bands). `2` = groups + leaves. Default `2`.
paletteTreemapPalette- `"mono"` (default) — one neutral shade for every leaf; groups separated only by the title band + gap. - `"sequential"` — leaf shade encodes the leaf's own value. - `"categorical"` — one hue per top-level group, ≤ 4 groups (else `"mono"`).
gapnumberPaper seam between tiles, in px. Default `2`.
labelMinAreanumberHide a tile's label below this area (px²). Never shrinks type — a label either renders at `text-chart-value`/`text-chart-source` size, or not at all.
labelOverflow"ellipsis" | "hide"How a name too long for its tile is handled (#280). - `"ellipsis"` (default) — clip it to the tile with `…`. - `"hide"` — draw it only when the WHOLE name fits (and, with `labels`, its value line too); otherwise draw nothing. Applies to leaf labels and group title bands alike — a truncated header is the same noise as a truncated tile name.
hideLeafLabel(leaf: TreemapLeafDatum) => booleanSkip the native name/value label for every leaf this predicate matches (#280) — for a tile a caller annotates itself (a `Leader` + `HaloText` callout) so the two labels never double up. Default `undefined` — every eligible leaf keeps its built-in label.
monoLeafColorstringOverride for `palette="mono"`'s one leaf shade (#280). The mono ramp is the same absolute lightness in every theme by design (`on-mark-ink.ts`), so it can look pale on a dark card; resolve a theme-appropriate step (`resolveThemeIsDark` + `var(--chart-mono-N)`) and pass it here when the default reads too light against your card. Default: the shared constant.
monoBandColorstringOverride for the `depth: 2` group title band's fill — same reasoning as {@link monoLeafColor}.
otherThresholdnumberMerge leaves under this share (0..1) of their parent's total into "Other". `0` (default) = off — the unconditional 30-leaf cap still applies.
drilldownbooleanWhen `true` (and `depth: 2`), clicking a group's title band zooms into that group (its leaves fill the canvas, animated); a "Back" control returns to the overview. Default `false` — a static chart has no click handler beyond `onDatapointClick`.
showValuesboolean@deprecated Use `labels` — `true`/`false` keep meaning the same thing (ADR 0042 A.3, row 15). Read until 7.0.0, with one development warning; when both are set, `labels` wins.
labelsboolean | ChartDataLabelsConfigPrint each labelled tile's formatted value on a second line under its name (#247). A tile shows the value only when its name is drawn AND it is tall enough for two lines and wide enough for the whole number — a value is never ellipsised or shrunk, it is either legible or absent. All visible values share ONE notation (the set is formatted together). Default `false` — area stays the only printed quantity unless you opt in.
valueFormatChartValueFormatHow leaf values render in the tooltip and (with `labels`) on tiles. Default `"compact"`.
classNamestring
styleCSSProperties
aspectRatiostringAspect ratio as "width / height". Default `"16 / 9"`.
plotHeightResponsive<ChartPlotHeight>The plot's own height (ADR 0039): px, or `{ aspect }` (width ÷ height), optionally per breakpoint. Wins over `aspectRatio`, which stays an alias.
accessibleLabelChartA11yProps["accessibleLabel"]Accessible name for the chart region (announces to AT on focus).
accessibleDescriptionChartA11yProps["accessibleDescription"]Supplemental description read by AT.
legendContainerLegendPropRenders a legend (RM-118). What it shows depends on `palette`: - `"categorical"` — one row per top-level GROUP, via `useContainerLegend` (placement + hover only, R3): hovering a row dims every OTHER group's tiles, never hides one. `depth: 2`'s title bands already NAME each group on the tile itself — this adds the colour key, not a second name. `{ values: true }` prints each group's total, in `valueFormat`. - `"sequential"` — leaf shade encodes a continuous VALUE, not a discrete category, so this renders `RampLegend` (the shared ramp key every sequential/diverging consumer uses — `ramp-legend.tsx`) instead, with its marker following the hovered leaf's value. - `"mono"` (default) — every leaf is the SAME shade; there is nothing to key, so a truthy `legend` renders nothing. Unset renders nothing (R1).
statusChartStatusShow the loading skeleton until the data is ready (ADR 0042 §9). Default `"ready"`.
emptyChartEmptyStateTitle and message shown when there is nothing to plot.
selectionStatesChartSelectionStatesResolver<Record<string, unknown>>Maps a mark (category, optional series key, optional raw row) to its selection state. Unset → the chart renders exactly as before.
dimExcludedbooleanDim `excluded` marks and add their non-hue channel (the dashed frame). Default `true`; `false` keeps the `data-selection` attribute (so a host can style it) but paints nothing.
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: ChartSelectionProps, 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>

ChartTooltipBox

PropTypeDescription
xrequirednumberAnchor X in pixels, relative to the container — a crosshair, a mark centre.
yrequirednumberAnchor Y in pixels, relative to the container.
visiblerequiredbooleanWhether the tooltip is visible
containerRefrequiredRefObject<HTMLDivElement | null>The chart container: the box is portaled into it and placed around it.
containerWidthrequirednumberContainer width in the chart's own pixels (scales `x`/`avoid` to the screen).
containerHeightrequirednumberContainer height in the chart's own pixels.
offsetnumberSpace kept between the box and the pointer or marks it avoids. Default 16.
classNamestringCustom class name
childrenrequiredReact.ReactNodeTooltip content
leftnumber | ReturnType<typeof useSpring>@deprecated Bypasses placement: the box is positioned inside the container at this left, as before placement existed, and can cover the pointer.
topnumber | ReturnType<typeof useSpring>@deprecated Bypasses placement: the box is positioned inside the container at this top, as before placement existed, and can cover the pointer.
flippedboolean@deprecated Only read with `left`/`top`: the side the entrance slides from.
springConfigSpringConfigPer-chart override; falls back to `ChartConfigProvider.tooltipBoxSpring`.
panelStyleReact.CSSPropertiesInline styles for the inner tooltip panel.
avoidChartTooltipAvoid | readonly ChartTooltipAvoid[]The hovered mark(s) the box must not cover — a dot, a bar's band, a cell, a node. Small marks are stepped around; a mark too large to avoid (a big treemap leaf) is covered by at most half. The pointer and a keyboard-focused target are always avoided without being listed here.
trackChartTooltipTrackHow the anchor moves: `"x"` for a crosshair scrubbing along x (the box keeps its top at `y`), `"y"` for rows, `"free"` (default) for a mark anywhere in the plot.
pinnedbooleanFreezes the placement while a touch tooltip is pinned (the finger has lifted).

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
bandvar(--chart-mono-2) — always neutral, even under palette: 'categorical', so grouping reads from the title band + gap alone, never from hue
leafMonovar(--chart-mono-4) — one shared shade for every leaf (the default)
leafSequential--chart-seq-1 … --chart-seq-7 by the leaf's own value
leafCategoricalone hue per top-level group (≤ 4 groups, else falls back to mono)
labelHaloText: the band title renders in the chart-source type role, the leaf label in the chart-value type role (RM-019, both pinned to the meta size step); the halo punches from var(--chart-background)
focusthe drilldown zoom/back buttons carry the shared compound focus indicator (--ring plus the --ring-contour outline), shown only on keyboard focus