# Treemaps

> Page: https://docs.plotwork.dev/treemaps. Index of every page: https://docs.plotwork.dev/llms.txt

A treemap draws nested areas sized by value: each tile is an item, its area is its value, and the frames round tiles are the groups they belong to. Use it to show where size, cost, or time sits in a hierarchy, such as a repository by folder, a cloud bill by service, or revenue by product line. It is for anyone, human or AI agent, who writes diagram source.

## TL;DR

| Question | Answer |
|---|---|
| What is it for? | Where a total goes. The biggest tile is the biggest part, and a frame shows what a branch adds up to. |
| How do I start one? | Write `treemap "Title"`, then `group "Name" {` blocks with `item "Name" 120` lines inside, and `}` to close each group. Items may also stand at the top level. |
| How is a value written? | A number above zero right after the name, with no quotes: `item "EC2" 12400`. Say what it counts once, on the header, with `unit="$"`. |
| How does it show change? | `delta="8%"` draws an arrow and the size of the change. `better=down` on the header makes a rise red, which is what a cost wants. |
| What is drawn? | A group is a frame with a corner tab that holds its icon, its name, and its total. An item is a tinted card with its icon, label, and value. Each top-level branch has its own tone, and a group inside a group is a stronger shade. |
| What if a tile is too small? | It shows what fits, whole: the label is never cut. A tile with room for nothing stays an empty card, and the text description lists every item. |
| What are the limits? | 300 items, 100 groups, and 4 groups one inside another. Past one, the source is refused with `E140`. More than 80 items, or many tiles too small for a label, draws a warning. |
| Does Mermaid convert? | Yes. `treemap-beta` and `treemap` become a Plotwork treemap. See [From Mermaid](#from-mermaid). |

## Ask your agent

```text
Use Plotwork to draw a treemap of our monthly cloud bill by service. Group the services into Compute, Data, Network, and Operations, give the amounts in dollars, and show the change since last month, with a rise in cost in red.
```

```text
Use Plotwork to draw a treemap of how big each folder of this repository is in kilobytes, with the packages and the apps as groups and the largest folders first.
```

## Example

**Diagram source**

```diagram
treemap "Monthly cloud spend" unit="$" better=down desc="Where the monthly cloud bill goes, by service, with the change since last month. A rise in cost is drawn red."

group "Compute" icon=lucide:cpu {
  item "EC2" 18400 icon=aws:ec2 delta="8%"
  item "Fargate" 6800 icon=aws:fargate delta="21%"
  item "Lambda" 4200 icon=aws:lambda delta="-12%"
  item "EKS" 3100 icon=aws:eks
}

group "Data" icon=lucide:database {
  item "RDS" 9600 icon=aws:rds delta="4%"
  item "S3" 5200 icon=aws:s3 delta="-3%"
  item "DynamoDB" 3900 icon=aws:dynamodb
  item "OpenSearch" 3400 icon=aws:opensearch
  item "ElastiCache" 2800
}

group "Network" icon=lucide:globe {
  item "Data transfer" 4800 delta="15%"
  item "NAT gateway" 3300 icon=aws:nat-gateway
  item "CloudFront" 2200 icon=aws:cloudfront delta="-6%"
  item "Load balancers" 1900 icon=aws:elb
}

group "Operations" icon=lucide:activity {
  item "CloudWatch" 4600 icon=aws:cloudwatch
  item "Support plan" 3200
  group "Security" icon=lucide:shield {
    item "GuardDuty" 2400 icon=aws:guardduty
    item "WAF" 1900 icon=aws:waf
  }
}
```

*Picture: Monthly cloud spend: 17 items in 5 groups, total $81,700. Where the monthly cloud bill goes, by service, with the change since last month. A rise in cost is drawn red.*

**Text description**
- Group Compute: $32,500, 40% of the total
  - EC2: $18,400, 23% of the total, up 8%
  - Fargate: $6,800, 8% of the total, up 21%
  - Lambda: $4,200, 5% of the total, down 12%
  - EKS: $3,100, 4% of the total
- Group Data: $24,900, 30% of the total
  - RDS: $9,600, 12% of the total, up 4%
  - S3: $5,200, 6% of the total, down 3%
  - DynamoDB: $3,900, 5% of the total
  - OpenSearch: $3,400, 4% of the total
  - ElastiCache: $2,800, 3% of the total
- Group Network: $12,200, 15% of the total
  - Data transfer: $4,800, 6% of the total, up 15%
  - NAT gateway: $3,300, 4% of the total
  - CloudFront: $2,200, 3% of the total, down 6%
  - Load balancers: $1,900, 2% of the total
- Group Operations: $12,100, 15% of the total
  - CloudWatch: $4,600, 6% of the total
  - Group Security: $4,300, 5% of the total
    - GuardDuty: $2,400, 3% of the total
    - WAF: $1,900, 2% of the total
  - Support plan: $3,200, 4% of the total

The four top-level groups take a tone each, in turn, and what is inside keeps it. The Security group sits inside Operations, so its frame is one step stronger than the frame round it. The green and red chips are changes since last month: the header says `better=down`, so a fall in cost is green and a rise is red. The line under the picture says the unit, the total, and how much there is of it.

## The language

One statement to a line. Text is always in double quotes, options are `key=value`, and `#` starts a comment. Indentation is for the reader and means nothing to the parser: nesting comes from the braces, so a line written at the wrong depth cannot silently move an item.

| Statement | Meaning | Example |
|---|---|---|
| `treemap "Title" unit=<unit> sort=<sort> values=<on\|off> better=<up\|down> desc="<text>"` | The header. Every option is optional, and so is the title. | `treemap "Cloud spend" unit="$" better=down` |
| `desc "<text>"` | The plain-language description, as a statement. The same as `desc=` on the header. | `desc "Where the bill goes."` |
| `group "<Name>" tone=<tone> icon=<icon> {` | Opens a frame. Write items and groups inside it, and close it with `}` on its own line. | `group "Compute" tone=blue icon=lucide:server {` |
| `item "<Name>" <value> tone=<tone> icon=<icon> delta=<change>` | A tile whose area is its value. | `item "EC2" 12400 icon=aws:ec2 delta="8%"` |
| `}` | Closes the nearest group that is still open, on a line of its own. | `}` |

A group is sized by what it holds, so it has no value of its own, and a group with nothing in it is refused. An item has no id, because a treemap has no cross references.

```plotwork
treemap "Disk use" unit="GB"

group "Home" icon=lucide:folder {
  item "Photos" 420 icon=lucide:folder
  item "Documents" 85
}

item "Backups" 310
```

### Header options

| Option | Values | Default | Meaning |
|---|---|---|---|
| `unit=` | a short word or symbol, at most 12 characters | none | What the values count. `$`, `€`, `£`, and `¥` lead the number (`$1,240`), `%` follows it (`85%`), and any other unit follows after a space (`120 MB`). |
| `sort=` | `desc`, `written` | `desc` | `desc` draws the largest first, which packs best. `written` keeps the order of the source, for an order that means something. |
| `values=` | `on`, `off` | `on` | `off` hides the value on each tile and the total in each tab. The line under the picture still says the total. |
| `better=` | `up`, `down` | `up` | Which way of a change is good news and so drawn green. Use `up` for sizes, coverage, and revenue, and `down` for cost and errors. |
| `desc=` | quoted text | none | The plain-language description. |

### Item and group options

| Option | On | Values | Default |
|---|---|---|---|
| `tone=` | item, group | `neutral`, `blue`, `violet`, `aqua`, `orange`, `green`, `amber`, `red` | the tone of the branch it is in |
| `icon=` | item, group | any bundled icon, such as `lucide:server` or `aws:lambda`, or `none` | none |
| `delta=` | item | a number, with an optional sign and an optional percent sign | none |

### Writing a change

A change is the difference since the last period, in the unit of the values or in percent. Its chip shows an arrow and the size of the change, so the arrow carries the sign. A rise can be a bare number. **A fall, and any percent, is written in double quotes**, because the language reads a bare `-` as part of an arrow and does not read a bare `%` at all.

| Written | Drawn |
|---|---|
| `delta=5` or `delta="+5"` | An up arrow and `5` |
| `delta="-5"` | A down arrow and `5` |
| `delta="12%"` | An up arrow and `12%` |
| `delta="-12.5%"` | A down arrow and `12.5%` |
| `delta="0"` | Nothing |

A bare `delta=-5` is refused with two errors, one for the `-` the lexer cannot read and one that says to quote it, so a fall is never drawn as a rise. The chip is green when its direction is the one `better=` names and red when it is not. When any item has a change, the line under the picture carries a key to the two chips.

### Sorting and order

By default the largest tile comes first and ties keep the order written, which packs the picture best and puts the biggest part where it is read first. `sort=written` keeps the order of the source instead, here the order the product lines were launched in. Revenue is good news when it rises, so `better=up` (the default) draws a rise green.

**Diagram source**

```diagram
treemap "Revenue by product line" unit="$" sort=written better=up desc="Yearly revenue by product line, in the order the lines were launched, with the change since last year. A rise is good news and drawn green."

group "Subscriptions" tone=blue icon=lucide:layers {
  item "Starter" 420000 delta="9%"
  item "Team" 880000 delta="22%"
  item "Enterprise" 1450000 delta="14%"
}

group "Usage" tone=aqua icon=lucide:server {
  item "API calls" 640000 delta="31%"
  item "Storage" 310000 delta="-4%"
  item "Egress" 190000 delta="-11%"
}

group "Services" tone=amber icon=lucide:users {
  item "Setup" 340000
  item "Training" 260000 delta="-18%"
  item "Support" 410000 delta="6%"
}
```

*Picture: Revenue by product line: 9 items in 3 groups, total $4,900,000. Yearly revenue by product line, in the order the lines were launched, with the change since last year. A rise is good news and drawn green.*

**Text description**
- Group Subscriptions: $2,750,000, 56% of the total
  - Starter: $420,000, 9% of the total, up 9%
  - Team: $880,000, 18% of the total, up 22%
  - Enterprise: $1,450,000, 30% of the total, up 14%
- Group Usage: $1,140,000, 23% of the total
  - API calls: $640,000, 13% of the total, up 31%
  - Storage: $310,000, 6% of the total, down 4%
  - Egress: $190,000, 4% of the total, down 11%
- Group Services: $1,010,000, 21% of the total
  - Setup: $340,000, 7% of the total
  - Training: $260,000, 5% of the total, down 18%
  - Support: $410,000, 8% of the total, up 6%

A value of 100,000 or more is drawn compactly on a tile (`$1.4M`, `$420K`), and in full in the text description.

### Colour and nesting

The top-level groups and items take `blue`, `orange`, `aqua`, `violet`, `amber`, and `neutral` in turn, and everything inside a group keeps its tone. `tone=` on a group or an item changes it for that branch. Green and red are left out of the rotation because they are what a change chip means, so a branch is never mistaken for news; choose them with `tone=` when they should mean something. A group inside a group is drawn one step stronger, to four levels, so a hierarchy of region, account, team, and service reads as four shades. Nobody writes a color value: the theme owns every color in light and dark.

Groups nest as the data does, with items at any level:

**Diagram source**

```diagram
treemap "Repository size by folder" unit="KB" sort=desc desc="How much of the repository each folder takes, in kilobytes, down to the files that matter."

group "packages" icon=lucide:package {
  group "diagrams" icon=lucide:folder {
    group "src" {
      item "kinds" 4800 icon=lucide:folder
      item "icons" 9200 icon=lucide:layers
      item "layout" 1300
      item "render" 1100
      item "parse" 900
      item "import" 1600
      item "constants" 2100
    }
    item "fixtures" 1400
    item "scripts" 650
  }
  item "ui" 2400 icon=lucide:box
}

group "apps" icon=lucide:monitor {
  item "docs" 7200 icon=lucide:book-open
  item "site" 3100 icon=lucide:globe
  item "api" 2600 icon=lucide:server
  item "mcp" 800
  item "app" 1500
  item "admin" 900
}

group "docs" icon=lucide:file-text tone=violet {
  item "architecture" 640
  item "research" 420
  item "standards" 310
}

item "pnpm-lock.yaml" 5600 icon=lucide:lock
item "node_modules cache" 2200
item "README and config" 180
```

*Picture: Repository size by folder: 22 items in 5 groups, total 50,900 KB. How much of the repository each folder takes, in kilobytes, down to the files that matter.*

**Text description**
- Group packages: 25,450 KB, 50% of the total
  - Group diagrams: 23,050 KB, 45% of the total
    - Group src: 21,000 KB, 41% of the total
      - icons: 9,200 KB, 18% of the total
      - kinds: 4,800 KB, 9% of the total
      - constants: 2,100 KB, 4% of the total
      - import: 1,600 KB, 3% of the total
      - layout: 1,300 KB, 3% of the total
      - render: 1,100 KB, 2% of the total
      - parse: 900 KB, 2% of the total
    - fixtures: 1,400 KB, 3% of the total
    - scripts: 650 KB, 1% of the total
  - ui: 2,400 KB, 5% of the total
- Group apps: 16,100 KB, 32% of the total
  - docs: 7,200 KB, 14% of the total
  - site: 3,100 KB, 6% of the total
  - api: 2,600 KB, 5% of the total
  - app: 1,500 KB, 3% of the total
  - admin: 900 KB, 2% of the total
  - mcp: 800 KB, 2% of the total
- pnpm-lock.yaml: 5,600 KB, 11% of the total
- node_modules cache: 2,200 KB, 4% of the total
- Group docs: 1,370 KB, 3% of the total
  - architecture: 640 KB, 1% of the total
  - research: 420 KB, <1% of the total
  - standards: 310 KB, <1% of the total
- README and config: 180 KB, <1% of the total

### What a tile shows

A tile shows the richest of these that it has room for, whole. A label is wrapped at word breaks, to at most three lines, and is never cut: a tile too narrow for its longest word shows no label, and the full text is in the text description.

| Rung | A tile shows |
|---|---|
| 1 | The icon on a chip, the label, the value, and the change |
| 2 | The label, the value, and the change |
| 3 | The icon on a chip, the label, and the value |
| 4 | The label and the value |
| 5 | The icon on a chip and the label |
| 6 | The label |
| 7 | The icon alone |
| None | An empty card |

A group's tab gives up its icon first, then its total, then cuts its name with an ellipsis and warning `W203`, as a flow group does. A frame too small for a readable name has no tab. A tile less than 4px wide or high is not drawn at all, and lint says which.

## Limits and warnings

| Limit | Value | What happens past it |
|---|---|---|
| Items | 300 | `E140` before any layout, with the advice to group the small ones into one "Other" item or split the treemap. |
| Groups | 100 | `E140`. |
| Groups one inside another | 4 | `E140` once for each group past the limit. Its items join the group around it, so nothing else is reported. |
| A value | above 0 and at most one trillion | `E136`. |
| A unit | 12 characters | `E136`. |
| Items before the density warning | 80 | `W200`. |
| Levels before the warning | 3 | `W200`. |

The picture is 880px wide, the width a Markdown column keeps text readable at, and the treemap in it grows taller with the number of items and groups, from 320px to 1100px. The layout is plain arithmetic, so a treemap of 300 items is laid out in a few milliseconds.

Every mistake comes back as a result with a code, a line, a column, and a hint. Warnings never stop the drawing.

| Code | When it fires | Fix |
|---|---|---|
| `E100` | An item with no value, an item name that is not in quotes, a group with no block or with no items, a treemap with no items, or text after a statement. | Read the hint, which names the line to write. |
| `E101`, `E110`, `E121`, `E122` | An unterminated string, an unknown statement, an unknown tone, or an unknown icon. | Read the hint, which names the nearest choice. |
| `E133`, `E134` | A group that is never closed, or a `}` with nothing to close. | Add or remove the brace. |
| `E135` | An unknown option, or `value=`. | Write the value right after the name: `item "Name" 120`. |
| `E136` | An unknown `sort=`, `values=`, or `better=`, a bad unit, a value that is not a number or is zero, or a `delta=` that is not a change. | Read the hint, which says what to write. |
| `E140` | More than 300 items, more than 100 groups, or groups nested more than 4 deep. | Split the treemap by top-level group, or flatten it. |
| `W200` | More than 80 items, groups nested more than 3 deep, many items too small to show a label, or items too small to draw. | Group the smallest items into one "Other" item, or split the treemap by group. |
| `W202` | The picture is wider than a Markdown column and its text would shrink. | Shorten the legend text, such as a long unit. |
| `W203` | A group name was cut with an ellipsis. | Shorten the name. |

## From Mermaid

Mermaid `treemap-beta` (and `treemap`, which Mermaid is renaming it to) converts to a treemap. A node with a value is an item and a node without one is a group, and a node is a child of the nearest node above it that is indented less, as Mermaid reads it. Ask your agent to convert it, or use the `convert_mermaid` tool. The [migration guide](https://docs.plotwork.dev/migrating-from-mermaid.md) covers the converter in full.

```mermaid-source
---
title: Product budget
---
treemap-beta
"Engineering"
    "Platform": 450000
    "Application": 380000:::important
    "Tooling": 0
"Marketing"
    "Campaigns": 210000
    "Events": 90000:::error
"Support": 160000

classDef important fill:#f96
```

The converter reports the class and its definition it dropped (`M200`), the zero it dropped (`M216`), and the tone it took from the class `error` (`M207`):

```plotwork
treemap "Product budget"

group "Engineering" {
  item "Platform" 450000
  item "Application" 380000
}

group "Marketing" {
  item "Campaigns" 210000
  item "Events" 90000 tone=red
}

item "Support" 160000
```

| Mermaid | Plotwork | Report |
|---|---|---|
| `"Name": 12` and `"Name", 12` | `item "Name" 12` | none |
| A name with nodes under it | `group "Name" {` | none |
| A value with `,` or `_` marks, such as `1,200` | `1200` | none |
| Single quotes and a trailing `;` | read as written | none |
| A `title` line, front matter `title:`, and `accTitle:` | The title. A `title` line wins. | none |
| `accDescr:` | `desc=` | none |
| `:::error` and the like | `tone=red` for a class that names a tone (`error`, `warning`, `success`, and the rest of the shared table) | `M207` |
| Any other `:::class`, `classDef`, and `style` | Dropped. | `M200` |
| Front matter `config:` such as `valueFormat` and `showValues`, and `%%{init}%%` | Dropped. Put the unit on the header with `unit=`. | `M200` |
| `<br/>` and other HTML in a name | Removed. A line break becomes a space. | `M205` |
| A leaf with a value of zero, a group with nothing left in it, and a node with no name | Dropped. | `M216` |
| A node under a leaf, a line that is not a node, a value that is not a number, and text after the header | Nothing is converted, or the line is skipped when the conversion is lenient. | `M110`, or `M218` |
| More than 300 nodes, or more than 4 groups one inside another | Nothing is converted. | `M102` |
| No title | Converted. | `M214` |

## See also

| Doc | Why |
|---|---|
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | The flow, sequence and schema languages, and the rules every kind shares |
| [Examples to copy](https://docs.plotwork.dev/examples.md) | Finished diagrams with the prompt that gets an agent to write each one |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | Convert existing Mermaid diagrams, and what is dropped |
| [Mind maps](https://docs.plotwork.dev/mind-maps.md) | Ideas that branch from one root, when the grouping matters more than the size |
| [Gantt charts](https://docs.plotwork.dev/gantt-charts.md) | A plan drawn as tasks along a calendar |
| [Timelines](https://docs.plotwork.dev/timelines.md) | Events along a time axis, when the order matters more than the size |
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every kind, node type, option and diagnostic |
| [Diagram kinds](https://docs.plotwork.dev/reference/language/kinds.md) | The kinds Plotwork draws and the header of each |
