_emailmd_
Directives

Breakdown

Ranked lists and breakdowns — a label, its value, and how far it moved.

A breakdown is a ranked list, or a total split into its parts: one row per list item, each a label, its value, and where you write one, how far it moved. The values line up down the right edge so they can be read as a column:

::: breakdown
1. Checkout: $4,210 (+12%)
2. Search: $3,980 (-3%)
3. Product page: $2,104
:::

Everything is drawn with table cells and text — no images, no SVG — so it renders in every client, adapts in dark mode, and becomes aligned columns in the text part.

Ranks

An ordered list ranks its rows, and a bulleted list doesn't. The rank is the number the list gives the row, so a list that starts at 4. ranks from 4. That makes it easy to send the next page of a leaderboard. A skipped item keeps its number, so each row keeps the rank written beside it.

Sub-labels

Any lines under an item become a smaller, muted sub-label under its label:

::: breakdown
1. Checkout: 4,210 (+12%)
   p95 182 ms
2. Search: 3,980 (-3%)
   p95 240 ms
:::

Only the first line is read as the label and value, so a sub-label can hold a colon of its own.

The change

A signed parenthetical at the end of a value is read as the change, and the sign becomes the arrow, as in stats. Up is the win by default. good=down flips the reading for the block, and {good=…} sets it for one row:

::: breakdown good=down
- Bounce rate: 41% (-3pt)
- Error rate: 0.8% (+0.2pt)
- Pages per visit: 3.2 (+0.4) {good=up}
:::

On a row with a sub-label, {good=…} can go at the end of the first line or the last. A neutral change takes the theme's muted_color, and good and bad changes its positive_color and negative_color. Rows without a change leave the column empty, and a block where no row has one leaves the column out.

Swatches and Bars

swatches=true puts a colored dot before each label, colored from the theme's chart_colors in order: the first row takes the first color, the second the second, and so on, starting again at the first when the rows outnumber the colors. That pairs the list with a chart image or a raw legend drawn in the same colors.

bars=true draws a bar under each row, scaled the way a chart is: against the largest value, or against 100 when every value is a percentage, or against max. A row's bar takes the same color as its swatch:

---
chart_colors: ["#e76e50", "#2a9d90", "#274754", "#e8c468", "#f4a462"]
---

::: breakdown swatches=true bars=true
- Direct: 4,200
- Organic search: 3,100
- Referral: 1,800
:::

A row's color is its own {color=…}, else the block's color, else with swatches its palette entry, else the theme's first chart color (or brand_color). color takes a hex color or a palette entry such as chart-2, and a palette entry follows dark mode. Without chart_colors, swatches can't tell rows apart, so every one takes the default color and the render warns.

A row whose value has no number to scale gets no bar, with a render warning.

Dividers

Rows are separated by hairlines in the theme's divider_color. dividers=false drops them.

Parameters

ParameterDescription
swatchestrue puts a palette-colored dot before each label. Defaults to false.
barstrue draws a bar under each row. Defaults to false.
dividersfalse drops the hairlines between rows. Defaults to true.
goodWhich direction is the win: up (default), down, or neutral.
colorSwatch and bar color, or chart-N for a palette entry. Defaults to the palette by position with swatches, and to the theme's first chart color without.
maxThe value a full bar represents. Defaults to the largest value, or 100 for percentages.
heightBar thickness. Defaults to 6px.
trackColor of the unfilled part of a bar. Defaults to the theme's card_color.
border-radiusBar end rounding. Defaults to half the bar height.

Per-item: {good=…} overrides the block's reading for one row, {color=…} recolors one row's swatch and bar.

Dark Mode

Labels and values take the dark palette's heading color, and ranks and sub-labels its muted color. Changes take its positive and negative colors, dividers its divider color, and bar grooves its card color. Swatches and bars in the default color or a palette entry switch to the dark palette's color. A hex color stays as written in both modes.

Plain Text

The text part keeps the columns, with the values lined up on their right edge, and draws bars in ASCII the way a chart does. Sub-labels sit on the line under their label. Swatches are color alone, so they are left out:

1.  Checkout      $4,210  ▲ 12%
    p95 182 ms
2.  Search        $3,980  ▼ 3%
3.  Product page  $2,104

Notes

  • A row needs both halves of Label: value on its first line. Items without a colon are dropped with a render warning, and a block with no usable items degrades to a regular text section.
  • The value is free text: Status: Healthy is a valid row, though it can't have a bar.
  • The label splits on the last colon of the first line.
  • Values and changes stay on one line, and the label column takes whatever room they leave. Labels wrap between words, and break inside a word only when the word is wider than that room, as a long label beside a large value and change can be on a narrow phone. Breaking the word keeps the row on the screen.
  • Text placed before the list renders above the rows.
  • In right-to-left documents the columns mirror, and bars grow from the right.
  • For a single number per tile, reach for stats; for bars with no ranks or changes, chart.

On this page