Skip to content
ShineUI
Install

Treemap

Composition by area.

<sui-chart-treemap>ChartsStable

About

Squarified treemap for a one-level composition, spend by category or revenue by segment: area is share of the total. Tiles step down the brand ramp in item order and one item may take the accent. Hovering a tile dims the others, outlines it in the accent and shows the exact figure and share.

Usage

<sui-chart-treemap heading="Revenue by segment" subtitle="Squarified treemap · area is share of the total · YTD 2026" caption="Parts flagged orange, up 22% against plan" money></sui-chart-treemap> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-chart-treemap"); el.items = [ { label: "Product sales", value: 412300 }, { label: "Service revenue", value: 268400 }, { label: "Maintenance contracts", value: 186900 }, { label: "Parts", value: 121500, accent: true }, { label: "Freight recovery", value: 64800 }, { label: "Training", value: 38200 }, { label: "Other", value: 21400 }, ]; /* SuiTreemapItem[] */ el.addEventListener("suiHover", (e) => console.log(e.detail)); </script>
import { SuiChartTreemap } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { const items = [ { label: "Product sales", value: 412300 }, { label: "Service revenue", value: 268400 }, { label: "Maintenance contracts", value: 186900 }, { label: "Parts", value: 121500, accent: true }, { label: "Freight recovery", value: 64800 }, { label: "Training", value: 38200 }, { label: "Other", value: 21400 }, ]; /* SuiTreemapItem[] */ return <SuiChartTreemap heading="Revenue by segment" subtitle="Squarified treemap · area is share of the total · YTD 2026" caption="Parts flagged orange, up 22% against plan" money items={items} onSuiHover={(e) => console.log(e.detail)} />; }
<script setup> import { SuiChartTreemap } from "@shineui/vue"; import "@shineui/core/shineui.css"; const items = [ { label: "Product sales", value: 412300 }, { label: "Service revenue", value: 268400 }, { label: "Maintenance contracts", value: 186900 }, { label: "Parts", value: 121500, accent: true }, { label: "Freight recovery", value: 64800 }, { label: "Training", value: 38200 }, { label: "Other", value: 21400 }, ]; // SuiTreemapItem[] const onChange = (e) => console.log(e.detail); </script> <template> <SuiChartTreemap heading="Revenue by segment" subtitle="Squarified treemap · area is share of the total · YTD 2026" caption="Parts flagged orange, up 22% against plan" money :items="items" @sui-hover="onChange" /> </template>
import { Component } from "@angular/core"; import { SuiChartTreemap } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiChartTreemap], template: `<sui-chart-treemap heading="Revenue by segment" subtitle="Squarified treemap · area is share of the total · YTD 2026" caption="Parts flagged orange, up 22% against plan" money [items]="items" (suiHover)="onChange($event)"></sui-chart-treemap>`, }) export class Example { items = [ { label: "Product sales", value: 412300 }, { label: "Service revenue", value: 268400 }, { label: "Maintenance contracts", value: 186900 }, { label: "Parts", value: 121500, accent: true }, { label: "Freight recovery", value: 64800 }, { label: "Training", value: 38200 }, { label: "Other", value: 21400 }, ]; // SuiTreemapItem[] onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

The plot is one tab stop named by an aria-label on the host, else by every tile with its exact figure, so a screen reader gets the whole composition without reading areas, and the arrow keys walk the tiles. Tiles step down --viz-1 to --viz-4 in declaration order and the one accent item takes --accent.

<sui-chart-treemap heading="Revenue by segment" subtitle="Squarified treemap · area is share of the total · YTD 2026" caption="Parts flagged orange, up 22% against plan" money></sui-chart-treemap>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-treemap').items = REVENUE; }

Counts

Without money the tiles and the tooltip print plain counts, which is the default; the money examples above set the attribute. The share percentage is unaffected. With money, currency="EUR" prints euros the way the page's locale writes them, and the share takes its percent sign.

<sui-chart-treemap heading="Open invoices by status" subtitle="Count of documents in the open ledger"></sui-chart-treemap>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-treemap').items = [ { label: 'Sent', value: 184 }, { label: 'Viewed', value: 96 }, { label: 'Partially paid', value: 41 }, { label: 'Disputed', value: 22, accent: true }, { label: 'Draft', value: 14 }, { label: 'On hold', value: 8 }, ]; }

Long tail

A tile prints its label and figure only when it is wider than 64px and taller than 34px, so a long tail stays clean. Hovering a small tile dims the rest and gives its label, exact value and share.

<sui-chart-treemap heading="Spend by vendor" subtitle="Three vendors carry the book · hover a small tile for its figure" caption="Small tiles drop their text and keep their tooltip" money></sui-chart-treemap>
// setup(root): properties and events setup(root) { root.querySelector('sui-chart-treemap').items = VENDORS; }

Events

suiHover fires when the hovered tile changes and reports { index }, the item's position in items rather than where the layout put it, with index -1 on leave. hoverIndex takes the same index, so a write from outside isolates the tile and shows its tooltip without emitting.

Idle.
<div style="display:flex;flex-direction:column;gap:12px"> <sui-chart-treemap id="tm-events" heading="Revenue by segment" subtitle="Hover a tile" money></sui-chart-treemap> <span id="tm-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Idle.</span> </div>
// setup(root): properties and events setup(root) { const el = root.querySelector('#tm-events'); const log = root.querySelector('#tm-log'); el.items = REVENUE; el.addEventListener('suiHover', (e) => { const i = e.detail.index; log.textContent = i < 0 ? 'Idle.' : `Item ${i}: ${REVENUE[i].label}, $${REVENUE[i].value.toLocaleString('en-US')}.`; }); }

Empty and loading

loading stands a shimmer in for the plot and marks the host aria-busy while the figures are on their way; the frame and its heading stay. A chart with nothing to plot keeps its frame and says so, and emptyMessage is the sentence. Both are the same on every chart.

<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px"> <sui-chart-treemap heading="Revenue by segment" subtitle="Fetching the year" height="150" loading></sui-chart-treemap> <sui-chart-treemap heading="Revenue by segment" subtitle="This year" height="150" empty-message="No revenue has been booked this year."></sui-chart-treemap> </div>

Keyboard

KeyAction
TabMoves to the plot.
ArrowRight ArrowDownMoves to the next tile, from the largest down, and shows its tooltip; a reader hears the item, its figure and its share.
ArrowLeft ArrowUpMoves to the previous tile.
Home EndMoves to the largest or the smallest tile.
EscapeLeaves the tile and hides the tooltip; the plot keeps focus.

API

<sui-chart-treemap>

Properties

PropertyAttributeTypeDefaultDescription
captioncaptionstringstring | undefinedFooter note, separated by a hairline.
currencycurrencystring'USD'The ISO 4217 code money prints in: USD by default, EUR, JPY. The page's locale (setLocale(), else <html lang>) places the symbol and sets the grouping and the decimal mark, so EUR on a German page reads 587.915 € on a tile and in the tooltip. An unknown code prints the bare figure. Ignored without money.
emptyMessageempty-messagestringstring | undefinedSentence drawn in place of the tiles when there are no items or every value is zero. Defaults to "Nothing to plot yet."; the frame and its heading stay. The chart filters nothing, so this is its only empty string.
fixedSizefixed-sizebooleanfalsePin the layout to width, which is what a fixed export wants, or a row of charts that have to measure the same whatever the space around them. Without it the tiles lay out across the width the container gives them and the coordinate space tracks the pixel width, so one unit stays one pixel: the tiles get wider, the labels keep the size they were drawn at, and the width a tile needs before it prints its label is real pixels. Reflected.
headingheadingstringstring | undefinedChart heading (the reference title prop, renamed because title is a native attribute). With what the chart shows, it names the plot for a reader; an aria-label or aria-labelledby on the host names the plot instead, and this summary then describes it.
heightheightnumber220Layout height in viewBox px.
hoverIndexhover-indexnumber-1Hovered item, as its position in items (not where the layout put it); -1 when idle. The pointer and the arrow keys set it, the arrows walking the tiles from the largest down, and the chart isolates the tile and shows its tooltip. A write from outside does the same and emits nothing, which is how sui-chart-synced mirrors a sibling that lists the same items.
itemsproperty onlySuiTreemapItem[][]Tiles to lay out; order drives the ramp step. A flat list: this is composition, not a hierarchy to drill into. A tile taller than 34px prints its label, wrapped to the lines its height holds and then ellipsized, over its figure, in whichever ink reads best on that tile's own color; a tile too narrow for six letters prints its figure alone, and the tooltip keeps the whole name. Property, not attribute.
loadingloadingbooleanfalseThe figures are still on their way: a shimmer stands in for the tiles, the frame and its heading stay, and the host is aria-busy. Reflected, so a page can style the wait.
moneymoneybooleanfalseFormat values as money in currency: compact ($588k) on tiles, exact in the tooltip. Off, the figures print plainly, which is what a count wants; set it for a balance. Reflected.
subtitlesubtitlestringstring | undefinedSecondary line under the heading.
widthwidthnumber460Layout width in px. The tiles lay out across the width the container gives them, and this is the starting figure, used until the container has been measured and wherever it measures zero; with fixedSize it is the layout's exact width and the frame is width + 34 wide.

Events

EventDetailReactVueDescription
suiHoverSuiChartHoverDetailonSuiHover@sui-hoverFires when the pointer or the keyboard moves the hover to another item; { index: -1 } when the pointer leaves the tiles, or Escape or a blur takes the keyboard's point away.

Methods

MethodSignatureDescription
setFocus()setFocus() => Promise<void>Moves focus to the plot, the chart's one tab stop, from which the arrow keys reach the tiles. Does nothing while there is nothing to plot.

Slots

SlotDescription
header-endControls at the end of the head row, after the heading.

Types

SuiChartHoverDetail interfaceDetail of suiHover: the hovered index, or -1 when the pointer leaves the plot. What the index counts is each chart's own: an x position here, a point on a scatter, an axis on a radar.
indexnumber
SuiTreemapItem interfaceOne tile. accent paints it in the accent: the single flagged category; color overrides the ramp.
labelstring
valuenumber
accent?boolean
color?stringThe tile's own color, which wins over accent; the tooltip key follows. Defaults to the ramp step for its place in items. Any CSS color or var(--token) works; use a token so the color follows the theme. Where the color is a token, a hex or an rgb(), the label takes whichever ink reads best on it, as on every other tile.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also