Specification
Every field of a chart specification, and the limits the renderer enforces.
schema/chartlet.schema.json
is the complete contract and can be used for editor validation. This page describes every field by
chart type, and the rules the renderer checks beyond the schema. An error names its code and the
place in the specification as a JSON Pointer (/flow/edges/2/to); see
Warnings and errors.
type |
Section | Drawn from |
|---|---|---|
bar, line |
Bar and line charts | data, or categories and series |
time |
Time charts | timeAxis, panes |
multiples |
Small multiples | panes |
stripes |
Stripes | stripes |
calendar |
Calendars | calendar |
rangebar |
Range bars | ranges |
boxplot |
Box plots | boxes |
timeline |
Timelines | timeline |
parliament |
Parliament charts | parliament |
treemap |
Treemaps | treemap |
sankey |
Sankey diagrams | sankey |
survival |
Survival curves | survival |
scatter |
Scatter plots | scatter |
waffle |
Waffle charts | waffle |
funnel |
Funnels and pyramids | funnel |
radar |
Radar charts | radar |
polar |
Polar charts | polar |
mosaic |
Mosaic charts | mosaic |
heatmap |
Heatmaps | heatmap |
waterfall |
Waterfalls | waterfall |
sequence |
Sequence diagrams | sequence |
flow |
Flow charts | flow |
state |
State diagrams | state |
classes |
Class diagrams | classes |
architecture |
Architecture diagrams | architecture |
tree |
Trees | tree |
network |
Networks | network |
venn |
Venn diagrams | venn |
valuemap |
Value chain maps | valuemap |
journey |
User journeys | journey |
storymap |
Story maps | storymap |
kanban |
Kanban boards | kanban |
fishbone |
Fishbone diagrams | fishbone |
cycle |
Cycles | cycle |
layers |
Layers | layers |
plan |
Plans | plan |
Fields every type takes are in Common fields, the value axis of the charts that have one in Value axis, and what the diagrams share in Diagram fields.
Common fields
| Field | Type | Required | Meaning |
|---|---|---|---|
schemaVersion |
1 |
yes | Always 1. |
type |
string | yes | One of the types in the table above. |
title |
string, up to 200 characters | yes | Visible title; also the accessible name of the chart (<title>). The SVG profile draws it in the chart: a title wider than the chart wraps onto a second line at a space, and the chart below it moves down; only what does not fit on two lines is shortened (text_truncated). The HTML profile shows it as the <figcaption> and draws no title in its SVGs, so it never shortens it. |
showTitle |
boolean | no | false leaves the drawn title out of the SVG profile when the page heads the chart, and gives its space to the chart; it stays the accessible name. Every chart type. The HTML profile never draws the title (its <figcaption> is the visible title), so it ignores showTitle. Default true. |
description |
string, up to 1000 characters | no | Replaces the generated description. |
source |
string, up to 300 characters | no | Shown below the chart in the HTML output. |
theme |
light, dark, blueprint |
no | light (default), dark or, for diagrams, blueprint; the palettes are CSS custom properties on the root. |
look |
classic, editorial |
no | classic (default) or editorial: neutral paper and ink with one accent, type tags, monospace technical text and unbordered label masks for diagrams; a chart that is not a diagram takes the paper and ink palette only. See Diagram design. |
tags |
boolean | no | false leaves the type tags off the nodes of an editorial diagram. Diagrams only; option_not_supported at /tags otherwise. |
tokens |
object | no | Single colors of the palette by the name of the --chartlet-* custom property without its prefix, such as { "accent": "#c2410c" }; hex or rgb()/rgba() values. They win over the theme and carry into the print and social variants and PNG. invalid_token at /tokens/<name> for another name or value. See Customize the look. |
stroke |
clean, sketch |
no | clean (default) or sketch: lines and shapes are displaced by up to about a pixel and a half and bowed slightly, for essays and slides. The displacement is seeded from the specification, so the bytes are the same every time; text, colors and contrast are untouched. The print variant and PNG draw clean strokes. See Customize the look. |
typography |
object | no | family (titles and labels), technicalFamily (technical sublabels such as the technology of a node) and widthScale (0.7 to 1.5, the measured width of the family relative to the built-in metrics). The families come first in the font stack, before Inter; the output names them but does not carry them (font_not_embedded). invalid_typography at /typography/<field> for a value that is not a plain family name or scale. See Typography. |
locale |
en, de |
no | en (default) or de: language of the generated texts (description, legend additions, tooltips, calendar month and weekday names, HTML caption and table) the number format (1,5) and dates: ISO 8601 (2026-08-31) in English, 31.08.2026 and 08.2026 in German. Available for every chart type. Negative numbers always use the true minus sign −. |
size |
doc, doc-wide, slide-16x9, slide-4x3, social-og, social-square, print-a4-landscape, print-a3-landscape, print-letter-landscape |
no | A named canvas for a destination, see Size presets. It sets width and height in CSS pixels (800 × 450, 1200 × 500, 1920 × 1080, 1600 × 1200, 1200 × 630, 1080 × 1080, 1123 × 794, 1587 × 1123, 1056 × 816; the paper sizes at 96 dpi) and a type scale: the chart is laid out at the canvas divided by the scale and drawn that much larger (slides 1.6, social-og 1.25, social-square 1.2, the others 1), so type, strokes, margins, label wrapping and legend rows follow the destination. A width or height in the specification overrides that side of the canvas. The print presets draw SVG with literal colors, as the print variant does. The mobile and social variants ignore size. Without size, or with doc or doc-wide, nothing changes. chartlet render --size <preset> and the size option of chartlet_render set it. A diagram that needs more room than the canvas keeps it and is reported as size_exceeded at /size. An unknown name is invalid_spec at /size, and the message lists the valid names. |
width, height |
integer | no | Size in pixels: 200–2400 × 160–1600, default 800 × 450. Below 320 pixels a chart is compact, for a panel in a grid of columns: a narrower value-axis gutter and margin keep room for the plot, and the text keeps its size when the chart is shown at the width it was rendered for. Text shrinks with the chart when a page scales it down, so render at the width of its place. |
mobile |
object | no | A second layout for narrow containers: { "width": 360, "height": 360, "breakpoint": 640 }. Available for every chart type. The SVG profile is unchanged; --variant mobile renders the mobile variant alone. See Responsive charts. |
mobile.width |
integer, 200–600 | with mobile |
Width of the mobile variant. |
mobile.height |
integer, 160–1600 | no | Height of the mobile variant, default 360. |
mobile.breakpoint |
integer, 320–1600 | no | The container width in CSS pixels below which the HTML profile shows the mobile variant, default 640. |
mobile.columns |
integer, 1–6 | no | Small multiples only: the grid columns of the mobile variant, usually 1. |
showValues |
boolean | no | Value labels on bars and points, default true. On a time pane with several layers the labels can overlap; the values stay in the tooltips and the data table. |
Rules:
schemaVersionother than 1 isunsupported_schema_version.- A size outside its limits is
invalid_dimensionwith the path of the field (/width,/height,/mobile/width,/mobile/height,/mobile/breakpoint); amobilewithoutwidthisinvalid_specat/mobile. - A mobile variant is laid out from the same specification at
mobile.width×mobile.height. Warnings that only the mobile layout raises are reported with the same code and path and a message that begins withmobile variant:. Asking for the mobile variant of a specification withoutmobileismissing_mobileat/mobile. "theme": "blueprint"is for sequence, flow, state, architecture, tree, class, network, cycle, layers and plan diagrams; any other type refuses it (option_not_supportedat/theme).- Texts must contain visible text (
empty_text), stay within their length (text_too_long) and hold no control character that XML 1.0 cannot represent (invalid_xml_character). - Numbers must be finite (
non_finite_value) and zero or of a magnitude between1e-100and1e100(unsupported_numeric_range). - A block that belongs to another type (
stripes,calendar,ranges,boxes,timeline,parliament,treemap,sankey,survival,scatter,classes,waffle,funnel,radar,polar,mosaic,heatmap,waterfall,sequence,flow,state,architecture,tree,network,cycle,fishbone,kanban,storymap,journey,valuemap,venn,layers,plan,columns), and a type’s own field on another type (showMean,violinScale,boxDisplay,references,stack,patterns,slope,rank,legend(endoutside a time chart,autoandnoneoutside a diagram),emphasis(outside a diagram),sparkline,tooltips,independentAxes,mobile.columns), is refused withoption_not_supportedrather than ignored. - A type whose block is absent is refused with the block’s own code:
missing_stripes,missing_calendar,missing_timeline,missing_parliament,missing_treemap,missing_sankey,missing_survival,missing_scatter,missing_waffle,missing_funnel,missing_radar,missing_polar,missing_mosaic,missing_heatmap,missing_waterfall,missing_sequence,missing_flow,missing_state,missing_classes,missing_architecture,missing_tree,missing_network,missing_cycle,missing_fishbone,missing_kanban,missing_storymap,missing_journey,missing_valuemap,missing_layers,missing_planandmissing_venn. An empty list where a type needs at least one entry isempty_data. - Stripes, calendars, timelines and every diagram are drawn from their block alone:
data,categories,series,zoomSteps,panes,orientation,categoryAxis,valueAxisandtimeAxisareoption_not_supportedthere.
Value axis
Bar, line, range bar, box plot and waterfall charts take a top-level valueAxis, small multiples one shared
by every panel (or one per panel, see Small multiples), and every pane of a
time chart its own. Bar and line charts also take categoryAxis.
| Field | Type | Required | Meaning |
|---|---|---|---|
categoryAxis.title |
string, up to 100 characters | no | Title of the category axis. |
valueAxis.title |
string, up to 100 characters | no | Title of the value axis. |
valueAxis.format |
number, percent |
no | number (default) or percent; 0.12 is shown as 12%. |
valueAxis.decimals |
integer, 0–6 | no | Fixed decimal places for values in tooltips, value labels, description and table. Axis ticks always carry as many decimals as their step (0.0, 0.5, 1.0). On a time chart every pane sets it on its own valueAxis. |
valueAxis.min, valueAxis.max |
number | no | The axis reaches at least down to min and up to max; values beyond them widen it further, and each end is rounded outward to a tick. Without them, a line, time series or range bar whose values are all zero or more never gets a margin below zero (and one whose values are all zero or less none above it). Bars always start at zero. On a time chart every pane sets them on its own valueAxis. |
valueAxis.thousandsSeparator |
boolean | no | true separates thousands in ticks, value labels, tooltips, description and table: 12,500, or 12.500 with locale: "de". Off by default, because four-digit values are often years. On a time chart every pane sets it on its own valueAxis. |
valueAxis.scale |
linear, log |
no | linear (default) or log: powers of ten evenly spaced, ticks at each power and, over two decades or fewer, at 2 and 5 times it. Every value, band and zone edge, reference line and declared min/max must be above zero; bars start at the bottom of the axis. Not with stack or an area layer. On a time chart every pane sets it on its own valueAxis. |
valueAxis.step |
number above 0 | no | The distance between ticks instead of a round step chosen from the values. |
valueAxis.exact |
boolean | no | true makes min and max the ends of the axis — both are required, every value has to lie between them, and the ticks sit on multiples of the step between them: { "min": -2.5, "max": 3.5, "step": 1, "exact": true } runs from −2.5 to 3.5 with ticks at −2, −1, 0 … 3 and the zero line. A bar chart’s exact axis includes zero. Not with a logarithmic axis or a stack. |
valueAxis.unit |
string, up to 20 characters | no | time (per pane) and multiples: the unit of the values, such as W/m². It follows the top tick label (4 W/m²) and the column names of the data table, and costs no height, unlike a title above the plot. |
valueAxis.reverse |
boolean | no | true runs the axis the other way, larger values down (or left on horizontal bars), as records such as δ18O are conventionally drawn. |
Rules:
minat or abovemaxisinvalid_axis_rangeatmax;decimalsabove 6 isinvalid_decimals.- A
stepof zero or less isinvalid_value; a step that gives more than 50 ticks across the values and declared ends, or fewer than two on an exact axis, isinvalid_axis_rangeatstep. exactwithout bothminandmax, or a bar chart’s exact axis that leaves out zero, isinvalid_axis_range; a value outside an exact axis isvalue_outside_axis.- On a logarithmic axis a value, edge or bound of zero or less is
invalid_value;logwith a stack or anarealayer,steporexactwithlog,exactwith a stack anduniton any other type thantimeormultiplesareoption_not_supported.
Bar and line charts
"type": "bar" and "type": "line". A chart has data (one series) or categories with
series, never both (conflicting_data_shape). Value axis and categoryAxis apply.
| Field | Type | Required | Meaning |
|---|---|---|---|
data |
array, 1–100 | one of | Single series: [{ "label": "…", "value": 1, "group": "…", "lower": 0.5, "upper": 1.5 }]. |
data[].label |
string, up to 200 characters | yes | The category. Labels are unique. |
data[].value |
number or null |
yes | The value; null marks a missing value (line charts). |
data[].group |
string, up to 60 characters | no | Bar charts: gives every bar of a group one palette color and a legend entry; either every point names a group or none does. |
data[].lower, data[].upper |
number | no | Bar charts without groups, and single-series line charts: an error bar through the end of the bar or the point, on every point or on none, the value between them. |
categories |
array of strings, 1–100 | one of | Unique category labels shared by all series. |
series |
array, 1–4 | with categories |
[{ "name": "…", "values": […] }]. |
series[].name |
string, up to 100 characters | yes | Legend and table label; unique. |
series[].values |
array of numbers or null |
yes | One value per category; null marks a missing value. |
series[].lower, series[].upper |
array of numbers or null |
no | A series of a bar or line chart without a stack: error bars, one entry per category, null where the value is null. |
orientation |
vertical, horizontal |
no | vertical (default) or horizontal; bar charts only. |
stack |
normal, percent |
no | bar with series only: "normal" stacks the series of a category by value, positive ones up and negative ones down, with the total beyond each stack; "percent" stacks shares of each category’s total (values of zero or more, axis in percent). A stack has no series filter. |
slope |
boolean | no | line with categories and series only: true draws a slope chart, one line per series from its first value to its second, with the name and value at both ends instead of a legend. Exactly two categories (invalid_categories), a value in every category (missing_value), no error bars or zoom steps; names that would overlap are spread apart. |
rank |
boolean | no | line with categories and series only: true plots the rank of each series in every category, 1 for the largest value at the top, with the names and ranks at both ends instead of a legend. At least two categories and two series (invalid_categories), a value in every category (missing_value), no value axis; equal values keep the order of the series. Not together with slope. |
patterns |
boolean | no | bar with series only: true draws every other series as an outline — the background inside, the series color around it — in the bars, the legend and stacks, so that series differ in form as well as in color. |
references |
array, up to 4 | no | bar and rangebar: reference lines across the bars, [{ "value": 48, "label": "EU average" }]. A line widens the value axis to reach its value; the description names it. |
references[].value |
number | yes | Where the line stands. |
references[].label |
string, up to 60 characters | yes | Its text. |
zoomSteps |
array, 2–4 | no | Variants selectable in the HTML output, each { "label", "from", "to" }: from and to are category indices. |
zoomSteps[].label |
string, up to 40 characters | yes | Name of the variant. |
zoomSteps[].from, zoomSteps[].to |
integer, from 0 | yes | First and last category of the variant. |
Rules:
- Up to 100 categories and four series (
too_many_data_points,too_many_series). Labels must be unique (duplicate_label), and so must series names (duplicate_series); a series has one value per category (series_length_mismatch). - A bar chart needs a value for every category of
data(missing_bar_value); a chart whose values are allnullisempty_series. - Groups: on every point or none (
missing_group), at most 4 (too_many_series), bar charts only and not together with error bars (option_not_supported). - Error bars:
lowerandupperon every point or value or on none (missing_bounds), the value between them (invalid_bounds); on another type or with a stackoption_not_supported. stackneeds categories with at least two series (option_not_supported); a percent stack takes no negative values (invalid_value).- More than four reference lines are
too_many_references. - Zoom steps: one alone is
not_enough_zoom_steps, more than fourtoo_many_zoom_steps; an index that is not a whole number from 0 isinvalid_spec,fromaftertoisinvalid_zoom_step,tobeyond the last categoryzoom_out_of_range. orientationon a line chart isoption_not_supported. More than 16 categories warn withdense_chart; when their labels do not fit even on two lines, only every few categories are labelled (labels_thinned). A bar chart category whose value labels have no room shows none (value_labels_omitted).
Time charts
"type": "time": one to four panes stacked on one shared time axis, each with its own valueAxis
(see Value axis) and layers. See the time series guide.
| Field | Type | Required | Meaning |
|---|---|---|---|
timeAxis.kind |
calendar, number |
no | calendar (default) reads every time as a timestamp. number reads it as a plain number — a distance along a profile, a depth, an age in millions of years — with ticks at round numbers, values written in the chart’s locale, and the axis title as the head of the table’s first column. No timezone, gaps: "collapse" or zoomSteps with it. |
timeAxis.timezone |
string | no | Fixed UTC offset such as "+02:00", or UTC (default). Timestamps without their own offset are read as wall-clock time in this zone. |
timeAxis.title |
string, up to 100 characters | no | Title of the time axis. |
timeAxis.gaps |
show, collapse |
no | show (default) keeps the distances in time; collapse places every observed timestamp at the same distance from the next, so weekends and holidays take no space. Reference lines, point markers and zones then take the slot of an observation and must lie within the observed range. |
timeAxis.precision |
year, month, day, minute |
no | How tooltips, the data table and the description name an observation. Absent, the coarsest form that tells the observations apart is chosen: a year when all fall on January 1, a month when all fall on the first of a month. Set it for annual values placed mid-year (1950-07-01 → 1950). Each data layer is named by its own observations (or its own precision), and a table row as finely as the finest layer with a value there. |
timeAxis.step |
number above 0 | no | The distance between time ticks instead of one chosen from the width: whole years (1–1000) on a calendar axis, units on a numeric axis; ticks sit on its multiples (50 → 1850, 1900, 1950, 2000). For a narrow panel whose claim needs a tick the automatic choice leaves out. A label that would run into the one before it is left out; its gridline stays. |
timeAxis.min, timeAxis.max |
time | no | Positions the time axis reaches at least, such as "1990" before data from 1991, so that a round tick is drawn. They extend the axis and never cut an observation off; they are no observations (no table row, not in the description). Not with "gaps": "collapse". |
timeAxis.reverse |
boolean | no | true runs the axis from right to left, the largest position at the left: ages before present read from the oldest on the left to today on the right. |
panes |
array, 1–4 | yes | The panes, top to bottom. |
panes[].heightRatio |
integer, 1–10 | no | The pane’s share of the plot height against the other panes, default 1. |
panes[].valueAxis |
object | no | title, format, decimals, min, max, step, exact, thousandsSeparator, scale, reverse and unit of the pane’s own value axis. |
panes[].stack |
normal, stream |
no | Stacks the pane’s area layers in layer order, each on top of the ones before it, so that the top edge shows their total; line and point layers in the pane stay as they are. Markers and the end labels sit on top of the stack; tooltips and the data table give each layer’s own value. stream stacks them around a moving baseline instead of zero, so that the stack is centered (a streamgraph): the value axis becomes a scale bar for thickness, the areas are named at their right ends, and the pane takes area layers and reference lines of time only, with no min, max, exact, reverse or logarithmic scale on its axis (option_not_supported). |
panes[].layers |
array | yes | Up to six data layers (eight in a stacked pane) and six annotation layers. |
sparkline |
boolean | no | true draws the lines and areas of one pane across the whole canvas, without axes, title, legend or value labels, with a dot at the end of each line; width 60–600 and height 16–200. The title and description remain the accessible name and description, and the HTML profile keeps its caption and table. Only line and area layers; no zoom steps or mobile variant. |
tooltips |
observations, markers |
no | "observations" (default) gives every observation a tooltip while observations stand at least 4 pixels apart, on an invisible target where no marker is drawn; "markers" gives tooltips only to drawn markers, so a line too dense for markers has none and its SVG stays small. |
legend |
top, end (time charts); auto, none (diagrams) |
no | Diagrams: see Diagram fields. Time charts: top (default) names the series in a legend above the plot; end writes each name at the last observation of its line, right of the plot, which makes room for the widest name (at most a third of the chart). Names that would overlap move apart. Not with candles, whose legend explains hollow and filled bodies. |
zoomSteps |
array, 2–4 | no | { "label", "from", "to" } windows selectable in the HTML output; from and to are times. Each window must hold at least two observations of one layer, and the chart is redrawn from the observations inside it. |
A time is Unix seconds between 1700-01-01 and 2200-01-01, an ISO 8601 date such as 2026-03-01 or
2026-03-01T12:00:00Z, or a bare year such as "1850" (January 1st).
Layers
Small multiples take the same layers, without ohlc.
| Field | Type | Required | Meaning |
|---|---|---|---|
mark |
line, area, point, ohlc, band, annotation |
yes | An area is a line whose region down to zero is filled in its color; the value axis then always includes zero. A point layer draws a dot for every observation and no line, as in a scatter plot; it takes no dash, stroke, curve, modeled or lower/upper. ohlc draws candlesticks (time only), band a zone, annotation a reference line or point marker. The area between two lines is an uncertainty band (lower/upper), not a separate mark. |
name |
string, up to 100 characters | one of | Legend and table label; required once a pane — or a time chart across its panes — has several data layers, and unique within the pane; on a time chart unique across all panes, since they share one legend. |
points |
array, 1–2000 | line, area, point | [{ "time": 1772323200, "value": 1 }]; ISO 8601 dates and bare years ("1850") are accepted too, with increasing time. "value": null marks a missing observation: the line, its area and its band break there, and table and description say “Missing”. |
points[].lower, points[].upper |
number | no | An uncertainty band: on every point with a value, or on none; a point without a value carries neither. |
data |
array, 2–2000 | ohlc | [{ "time": "2026-03-02", "open": 10, "high": 12, "low": 9, "close": 11 }], candles with increasing time; low at or below open and close, high at or above them. A candlestick layer takes no color, modeled, stroke or dash. |
color |
string, up to 100 characters | no | #rgb, #rrggbb, #rrggbbaa, var(--name), or var(--name, #hex) with a hex fallback, which the print variant draws. Anything else is painted in the neutral tone and warns with color_not_supported; a var(--name) without fallback is drawn in the text color by the print and social variants, with color_not_resolved. |
modeled |
boolean | no | Line and area layers: the line is dashed (unless dash says otherwise), its band hatched, and legend, description and table say “modelled”. |
stroke |
regular, thin, medium, bold |
no | Line and area layers: regular (default, 3 px), thin (1 px), medium (2 px, for small panels) or bold (4.5 px); the legend sample shows the same weight, so two lines differ in more than color. |
dash |
solid, dashed, dotted |
no | Line and area layers. Defaults to dashed for a modeled layer and solid otherwise; the legend sample shows the same pattern. |
curve |
linear, step |
no | Line and area: linear (default) joins the observations directly; step holds each value until the next observation, for values that stand for a whole period such as an annual mean. |
stepEnd |
time | no | line and area with "curve": "step": a time after the last observation where its step ends — annual means drawn as steps end at the end of their last year. It is no observation: no marker, tooltip, table row or description. Not with collapsed gaps or in a stacked pane. |
markers |
boolean | no | line and area only: false draws no markers, for a fitted or reference line whose observations are not data; tooltips stay on invisible targets unless the chart sets "tooltips": "markers". |
precision |
year, month, day, minute |
no | Data layers on a calendar axis: the same values as timeAxis.precision, for one layer — yearly means beside monthly values. On a numeric axis each layer writes its positions with the decimals its own positions need. |
value, time |
number, time | annotation | A horizontal reference line at value, a vertical one at time, or a point marker at both. Both widen the axes. |
shape |
circle, square, diamond, triangle-up, triangle-down |
no | Point markers only, circle by default. |
bottom, top |
number | band | Value edges of a zone; bottom must lie below top. A missing edge is the edge of the plot. Given edges widen the value axis. |
from, to |
time | band | Time edges of a zone; from must lie before to. A missing edge is the edge of the plot. At least bottom and top, or from and to, are required. |
label |
string, up to 100 characters | annotation, band | Required text of a reference line, point marker or zone. |
Rules:
- One to four panes (
too_many_panes), each with aheightRatiofrom 1 to 10 (invalid_height_ratio) and up to six data layers (eight in a stacked pane) of 2000 observations or candles each (too_many_data_points). At most four data layers of the whole chart take a palette color — candles take none — and a further layer needs its owncolor(too_many_layers). Layer names are unique across the panes (duplicate_series), and every data layer needs one once the chart has more than one (missing_name). - Observations: a time that cannot be read is
invalid_time; times must increase (unordered_time); a layer without observations, or a line, area or point layer with fewer than two, isempty_series; a candlestick layer needs at least two candles (empty_series). AstepEndat or before the last observation isunordered_time. - Bands:
lowerandupperon every point with a value or on none (incomplete_band),uppernot belowlower(invalid_band); a value outside its own band warns withvalue_outside_band. - A candle whose
lowlies above its open or close, or whosehighlies below them, isinvalid_candle; more candles than a third of the plot’s horizontal pixels are drawn as wicks only and reported asdense_chart. - A stacked pane needs area layers with the same times and
curve, a value of zero or more at every time (unaligned_stack,negative_in_stack) and nolower/upper. - A pane takes up to six annotation layers — zones, reference lines and point markers together
(
too_many_annotations). A zone needsbottomandtop,fromandto, or both, and a reference linetime,valueor both (missing_position);bottomat or abovetop, orfromat or afterto, isinvalid_band; a zone, reference line or point marker withoutlabelismissing_label. A field that belongs to another mark —name,points,valueorshapeon a zone,shapeon a reference line — isoption_not_supportedwith its path. A label that overlaps another annotation label, crosses a data line, or leaves the plot is reported as alabel_overlapwarning. - With
"gaps": "collapse", a reference line or point marker outside the observed range and a zone that encloses no observation aretime_out_of_range. timeAxis:maxat or beforeminisinvalid_axis_range; astepthat is no whole number of years from 1 to 1000 on a calendar axis, not above zero on a numeric one, or that places no tick or more than 50, isinvalid_axis_rangeat/timeAxis/step. Atimezonethat is neitherUTCnor a fixed offset isinvalid_timezone.- Zoom steps: one alone is
not_enough_zoom_steps, more than fourtoo_many_zoom_steps,fromat or aftertoisinvalid_zoom_step, and a window has to hold two observations of a layer in every pane (zoom_out_of_range). titleon a pane, and the bar and line fieldsdata,categories,series,orientationandcategoryAxis, areoption_not_supportedon a time chart.- Markers and value labels are drawn up to 60 observations per layer; a denser layer is a line
only, and the values stay in the data table. More observations than the plot has horizontal
pixels produce a
dense_chartwarning. These numbers come from a measurement at the default size — see the time series guide.
Small multiples
"type": "multiples": 2–12 panes in a grid, each with a unique title, sharing the top-level
valueAxis. A pane either has layers (as on a time chart, without ohlc), or, with
top-level categories, its own values — small multiples of bars.
| Field | Type | Required | Meaning |
|---|---|---|---|
panes |
array, 2–12 | yes | The panels, in reading order. |
panes[].title |
string, up to 100 characters | yes | Heading of the panel. Panel titles take two lines before they are shortened; every panel keeps the same head height, so the plots stay aligned. |
panes[].note |
string, up to 160 characters | no | A short finding under the panel title, such as a verdict, on up to two lines. The notes are part of the description. |
panes[].noteEmphasis |
boolean | no | true draws the panel’s note in the text color and semibold instead of muted, to set one finding apart from the others by weight, not by color alone. Needs a note. |
panes[].layers |
array | one of | Up to six data layers and six annotation layers. |
panes[].values |
array of numbers or null, up to 100 |
one of | With categories: one value per category, null for none. |
panes[].valueAxis |
object | no | With categories: the panel’s own value axis. |
categories |
array of strings, 1–20 | no | The categories of small multiples of bars. |
columns |
integer, 1–6 | no | Grid columns; default up to three. |
independentAxes |
boolean | no | true gives every panel its own value axis, scaled to its own values, for panels in different units or sizes. Their heights can then no longer be compared across panels; the description says so. |
Rules:
- Fewer than two panes is
not_enough_panes, more than twelvetoo_many_panes;columnsoutside 1–6 isinvalid_columns. A pane withouttitleismissing_title, two alikeduplicate_title. - At most four distinct layer names across all panels (one palette color each).
- Small multiples of bars: at most 20 categories (
too_many_data_points), unique (duplicate_label); a panel of bars takesvalues, nolayersorstack(option_not_supported), and draws horizontal bars (orientationisoption_not_supported). zoomStepsand theohlcmark areoption_not_supported, and so areheightRatioandstack, which belong to a time chart;noteandnoteEmphasisbelong to small multiples only.
Stripes
"type": "stripes". Stripes and calendars share one diverging scale.
| Field | Type | Required | Meaning |
|---|---|---|---|
stripes.firstYear |
integer | yes | The year of the first value; the values follow year by year. |
stripes.values |
array of numbers or null, 1–500 |
yes | One value per year; null leaves a year empty. |
stripes.reference |
number | no | The neutral middle of the scale, default 0. |
stripes.min, stripes.max |
number | no | Where the outermost steps begin; default the largest distance of any value from the reference. |
stripes.yearLabels |
boolean | no | Label the first and the last year under the stripes, default true. |
stripes.stretch |
boolean | no | true draws the stripes alone across the whole canvas, without margins, title or year labels, and the SVG stretches to whatever box the page gives it (preserveAspectRatio="none"): give it a height in CSS, such as a 6-pixel band. Title and description remain its accessible name and description. |
Rules: up to 500 yearly values (too_many_data_points); every year between 1 and 9999
(invalid_year); at least one value that is not null (empty_series); min below and max
above the reference (invalid_scale).
Calendars
"type": "calendar": a heatmap of one year on the diverging scale.
| Field | Type | Required | Meaning |
|---|---|---|---|
calendar.year |
integer, 1700–2199 | yes | The year shown. |
calendar.layout |
months, weeks |
no | months (default): twelve rows of up to 31 days; weeks: seven weekday rows, one column per week. |
calendar.days |
array, 1–366 | yes | [{ "date": "2024-03-01", "value": 1 }]. A day that is absent or null stays an empty cell. |
calendar.days[].date |
string | yes | ISO 8601 date in the chart’s year. |
calendar.days[].value |
number or null |
yes | The day’s value. |
calendar.reference, calendar.min, calendar.max |
number | no | As for stripes. |
Rules: a year outside 1700–2199 is invalid_year; a date that is no ISO 8601 date is
invalid_date, one in another year date_outside_year, a day given twice duplicate_date; at
least one value that is not null (empty_series); min below and max above the reference
(invalid_scale).
Range bars
"type": "rangebar": one span per category. orientation, Value axis and
references apply. See Forest plots.
| Field | Type | Required | Meaning |
|---|---|---|---|
ranges |
array, 1–100 | yes | [{ "label": "…", "low": 0, "high": 1, "mid": 0.5, "modeled": false, "group": "…", "weight": 12, "summary": false }]. |
ranges[].label |
string, up to 200 characters | yes | The category; unique. |
ranges[].low, ranges[].high |
number | yes | The ends of the span. |
ranges[].mid |
number | no | A central mark between low and high. |
ranges[].modeled |
boolean | no | Draws the span as modelled. |
ranges[].group |
string, up to 60 characters | no | Draws the spans of each group in its own palette color, with a legend entry per group. |
ranges[].weight |
number above 0 | no | With mid: turns the central mark into a square whose area follows it. |
ranges[].summary |
boolean | no | With mid: draws the span as a diamond, widest at mid — together with references a forest plot. |
| display | bar, dumbbell | no | Draws each span as a bar (the default) or as a dumbbell: an open dot at low, a filled dot at high and a line between them, both values written beside their dots. |
| sort | none, change | no | change puts the widest span first, in the picture, the table and the description; none (default) keeps the order as listed. |
Rules: a dumbbell takes no mid, weight, summary or modeled on a range
(option_not_supported, with the path of the field), and display and sort belong to range bars
only (option_not_supported); up to 100 ranges (too_many_data_points) with unique labels (duplicate_label); low
above high is invalid_range, a mid outside them mid_outside_range; groups on every range or
none (missing_group), at most 4 (too_many_series); a weight of zero or less is
invalid_weight, and weight or summary without mid is invalid_weight or
summary_without_mid. More than 16 ranges warn with dense_chart.
Box plots
"type": "boxplot". orientation and Value axis apply. See
Box plots.
| Field | Type | Required | Meaning |
|---|---|---|---|
boxes |
array, 1–100 | yes | [{ "label": "…", "values": [3, 4, 5, 6, 9] } or { "label": "…", "min": 1, "q1": 2, "median": 3, "q3": 4, "max": 6, "outliers": [9] }]. |
boxes[].label |
string, up to 200 characters | yes | The category; unique. |
boxes[].values |
array of numbers, 5–1000 | one of | Observations from which the box is computed: quartiles by linear interpolation, whiskers to the last value within 1.5 × the box, every other value a point. |
boxes[].min, boxes[].q1, boxes[].median, boxes[].q3, boxes[].max |
number | one of | The five numbers of a box computed elsewhere, in this order. |
boxes[].outliers |
array of numbers | no | Points beyond the whiskers of a box given by its five numbers. |
boxes[].group |
string, up to 60 characters | no | On every box (up to four groups): colors the boxes of a group alike, with a legend. |
boxDisplay |
box, violin, strip, beeswarm, ridgeline |
no | Draws the observations as boxes (the default), as violins, as strips, as a beeswarm (dots moved sideways until none overlap) or as a ridgeline (one density curve per box, stacked with a slight overlap); every display but box needs values on every box. |
showBox |
boolean | no | Beeswarms only: draws the box and its whiskers under the dots. |
violinScale |
width, area, count |
no | Violins only: width (default) makes every violin as wide as the others at its widest, area gives them the same area, count an area that follows their number of observations. |
showMean |
boolean | no | Marks the mean of every box with a diamond and adds a Mean column to the table; needs values. |
Rules: 1–100 boxes (too_many_data_points) with unique labels (duplicate_label); values and
the five numbers never both (conflicting_data_shape), never part of the five numbers
(incomplete_box), the five numbers in order (invalid_box), and 5–1000 values
(invalid_values); violins, strips, beeswarms, ridgelines and showMean need values on every
box (values_required); showBox belongs to beeswarms and violinScale to violins
(option_not_supported); groups on every box or none (missing_group), at most 4
(too_many_series). More than 16 boxes warn with dense_chart.
Timelines
"type": "timeline": phases, milestones, marker lines and which items follow which. See
Timelines.
| Field | Type | Required | Meaning |
|---|---|---|---|
timeline.items |
array, 1–60 | yes | [{ "id": "a", "label": "…", "start": "2027-01-04", "end": "2027-02-26", "group": "…", "after": ["b"] }, { "label": "…", "at": "2027-06-15" }]. |
timeline.items[].id |
identifier | no | What after and rollup name; unique. |
timeline.items[].label |
string, up to 100 characters | yes | The row’s name. |
timeline.items[].start, timeline.items[].end |
day | phase | A phase runs from start to end. |
timeline.items[].at |
day | milestone | A milestone lies at at. |
timeline.items[].progress |
number, 0–1 | no | How much of a phase is done: that share of its bar is drawn darker. |
timeline.items[].rollup |
array of ids | no | The ids of the phases this phase sums up: its progress is theirs, each weighted by its length. Instead of progress. |
timeline.items[].group |
string, up to 60 characters | no | Groups on all items or none, at most 4. |
timeline.items[].after |
array of ids | no | The ids of the items this item follows. |
timeline.markers |
array, up to 6 | no | [{ "label": "Today", "at": "2027-02-10" }]: dashed marker lines for days that matter. |
timeline.markers[].label |
string, up to 60 characters | yes | The marker’s text. |
timeline.markers[].at |
day | yes | Its day. |
timeline.groupRows |
boolean | no | Puts the rows of a group together, in the order the groups first appear (needs groups). |
timeline.timezone |
string | no | Reads and writes the days in UTC (default), a fixed offset such as +02:00 or one of about 55 IANA names such as Europe/Berlin, with the daylight saving rules in force today. |
timeline.weekends |
boolean | no | Shades Saturdays and Sundays on an axis of at most 120 days. |
Days are ISO 8601 dates, optionally with a time, or Unix seconds.
Rules: 1–60 items (too_many_nodes) and up to 6 markers (too_many_markers); ids are identifiers
(invalid_id) and unique (duplicate_id); a day that cannot be read is invalid_time, an end
before its start invalid_range; an item is a phase or a milestone, never a mix
(invalid_item); progress outside 0–1 is invalid_value, on a milestone (or a milestone with
rollup) progress_on_milestone, together with rollup conflicting_progress; a rollup
names existing items (unknown_node) that are phases summing up no others, not itself
(invalid_rollup); after names existing items (unknown_node) and runs in no circle
(circular_dependency), and an item that starts before one it follows ends warns with
follows_overlap; groups on all items or none (missing_group), at most 4 (too_many_series),
and groupRows without groups is option_not_supported; an unknown timezone is
invalid_timezone; weekends beyond 120 days warns with weekends_hidden; more than 30 items
warn with dense_chart.
Parliament charts
"type": "parliament": the seats of an assembly as dots in a semicircle, in blocks by party from
left to right in the order of the list. See
Parliament charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
parliament.parties |
array, 1–8 | yes | [{ "label": "Red", "seats": 40 }, { "label": "Blue", "seats": 35 }]. |
parliament.parties[].label |
string, up to 100 characters | yes | The party; unique. |
parliament.parties[].seats |
integer, 1–800 | yes | Its seats. |
parliament.majority |
boolean | no | Marks the seat that makes a majority. |
parliament.coalition |
array of labels | no | Labels of parties whose seats are ringed and summed. |
Rules: up to 8 parties (too_many_series) with unique labels (duplicate_label) and at least
one seat (invalid_value), at most 800 seats (too_many_data_points); coalition names
existing parties (unknown_node).
Treemaps
"type": "treemap": the areas follow the values by the squarified method. See
Treemaps.
| Field | Type | Required | Meaning |
|---|---|---|---|
treemap.items |
array | yes | [{ "label": "Schools", "value": 310, "group": "Society" }]. |
items[].label |
string, up to 100 characters | yes | The item; unique, parts included. |
items[].value |
number above 0 | leaf | The value of a leaf; an item with children has none. |
items[].group |
string, up to 60 characters | no | On every item or on none, up to 4 groups: colors the rectangles with a legend. |
items[].children |
array | no | The parts of the item, drawn inside its rectangle, nested up to three levels; colored by the item at the top. |
Rules: up to 100 items, parts included (too_many_data_points), with unique labels
(duplicate_label) and values above zero (invalid_value); an item with children takes no
value (value_with_children), nested at most three levels (too_deep), and a nested treemap
takes no groups (group_with_children); groups on every item or none (missing_group), at most 4
(too_many_series).
Sankey diagrams
"type": "sankey". Columns follow the longest path to a node; a band takes the color of the node
it leaves. See Sankey diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
sankey.links |
array, 1–100 | yes | [{ "from": "Gas", "to": "Power", "value": 120 }]. The nodes are the labels the links name, in the order they first appear. |
sankey.links[].from, sankey.links[].to |
string, up to 100 characters | yes | The nodes the flow leaves and reaches. |
sankey.links[].value |
number above 0 | yes | How much flows. |
sankey.links[].back |
boolean | no | A flow back to an earlier column, drawn as a loop under the diagram; the columns and their order do not follow it. |
sankey.nodes |
array, up to 40 | no | Nodes whose column or color the specification fixes; they come first, in this order. |
sankey.nodes[].label |
string, up to 100 characters | yes | A label the links name. |
sankey.nodes[].column |
integer, 0–7 | no | Pins the node’s column. |
sankey.nodes[].color |
integer, 1–4 | no | Pins its palette color. |
sankey.order |
auto, listed |
no | auto (default) orders the nodes of a column to cross few bands; listed keeps the order of nodes, then of the links. |
Rules: up to 100 links (too_many_data_points) and 40 nodes (too_many_nodes); values above zero
(invalid_value), no link from a node to itself (self_link), one link for each pair
(duplicate_link), no cycle (link_cycle) except flows marked "back": true, which must return
to an earlier column (link_not_back); listed nodes have unique labels (duplicate_label) that
the links name (unknown_node), and a pinned column no earlier than the links into the node allow
(column_too_early).
Survival curves
"type": "survival": Kaplan-Meier estimates. See
Survival curves.
| Field | Type | Required | Meaning |
|---|---|---|---|
survival.groups |
array, 1–4 | yes | [{ "label": "Standard", "observations": [{ "time": 4.2 }, { "time": 9, "event": false }] }]. |
survival.groups[].label |
string, up to 100 characters | yes | The group; unique. |
survival.groups[].observations |
array, 2–2000 | yes | The observed times. |
survival.groups[].observations[].time |
number, 0 or more | yes | When the event happened, or the observation ended. |
survival.groups[].observations[].event |
boolean | no | false cuts the observation off (censored), marked on the curve; default true. |
survival.confidence |
boolean | no | Draws the 95 % band (log-log, Greenwood variance). |
survival.atRisk |
boolean | no | Writes the number at risk under the time axis, default true. |
survival.logRank |
boolean | no | Tests whether the groups differ (log-rank, Mantel-Cox) and writes the statistic and p in the plot and the description. |
survival.timeTitle |
string, up to 100 characters | no | Title of the time axis, such as Months. |
Rules: 1–4 groups (too_many_series) with unique labels (duplicate_label) and 2–2000
observations each (invalid_values); a time is zero or more (invalid_value); logRank needs
two or more groups (log_rank_needs_groups).
Scatter plots
"type": "scatter". See Scatter plots.
| Field | Type | Required | Meaning |
|---|---|---|---|
scatter.points |
array, 1–5000 | yes | [{ "x": 1.2, "y": 3.4, "group": "Up", "label": "Gene A" }]. From 300 points on the dots are small and have no tooltip; from 100 the data table lists only the labeled points, or the 20 highest. |
scatter.points[].x, scatter.points[].y |
number | yes | The position. |
scatter.points[].group |
string, up to 60 characters | no | On every point or none, up to 4 groups with a legend. |
scatter.points[].label |
string, up to 60 characters | no | Writes a name beside the point where there is room. |
scatter.points[].size |
number above 0 | no | On every point or none (missing_size): the area of the point is proportional to it, with a size legend of three reference circles; adds a column to the table. |
scatter.sizeTitle |
string, up to 60 characters | no | What the sizes are, named in the size legend, tooltips and table; default the word for a size. Needs sizes on the points (option_not_supported). |
scatter.lines |
array, up to 8 | no | [{ "axis": "y", "value": 1.3, "label": "p = 0.05" }]: lines across the plot. |
scatter.lines[].axis |
x, y |
yes | The axis the value lies on. |
scatter.lines[].value |
number | yes | Where the line stands. |
scatter.lines[].label |
string, up to 100 characters | no | Its text. |
scatter.xTitle, scatter.yTitle |
string, up to 100 characters | no | Axis titles. |
scatter.xRange, scatter.yRange |
[min, max] |
no | Fix the ends of an axis instead of ends rounded outward to a tick; everything drawn along it must lie within. |
scatter.xTicks |
array, up to 40 | no | [{ "at": 1, "label": "chr1" }]: named labels at chosen places on the x axis instead of numbered ticks; a label that would run into the one before it is left out. |
scatter.xTicks[].at |
number | yes | The position. |
scatter.xTicks[].label |
string, up to 40 characters | yes | The name. |
scatter.quadrants |
object | no | { "x": 5, "y": 5, "labels": { "topLeft": "Quick wins" } } divides the plot into four areas by a line at x and one at y, and says in the description and the table in which area each point lies. |
scatter.quadrants.x, scatter.quadrants.y |
number | yes | The dividing lines. |
scatter.quadrants.labels.topLeft, .topRight, .bottomLeft, .bottomRight |
string, up to 40 characters | no | The name of each area, each optional; top is the side of the larger y, right the side of the larger x. |
scatter.regression |
boolean | no | Draws the least-squares line for each group; on a logarithmic axis it is fitted to the logarithms. |
scatter.xScale, scatter.yScale |
linear, log |
no | log needs values above zero. |
Rules: 1–5000 points (too_many_data_points) with finite numbers; groups on every point or none
(missing_group), at most 4 (too_many_series); more than 8 lines or 40 tick labels are
too_many_references; with a logarithmic scale every value is above zero (invalid_value); a
range whose ends are not in order is invalid_range, and what lies outside a fixed range —
points, lines, ticks, the dividing lines of quadrants — is out_of_range. A dividing line on the
end of an axis, or an area too small for its name, warns with quadrant_without_room.
Waffle charts
"type": "waffle": a grid of squares that each stand for the same share of a whole, filled by the
parts in reading order. See Waffle charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
waffle.parts |
array, 1–4 | yes | [{ "label": "Wind", "value": 31 }, { "label": "Gas", "value": 28 }]; each part gets the share of the squares by the largest remainder, and at least one. |
waffle.parts[].label |
string, up to 100 characters | yes | The part; unique. |
waffle.parts[].value |
number above 0 | yes | Its amount. |
waffle.cells |
integer, 10–400 | no | The number of squares, default 100. |
waffle.columns |
integer, 2–40 | no | Squares per row; default about square. |
waffle.total |
number above 0 | no | A total above the sum of the parts leaves squares for the rest. |
Rules: up to 4 parts (too_many_series) with unique labels (duplicate_label) and values above
zero (invalid_value); cells outside 10–400, or no more cells than parts, is
invalid_cells; columns outside 2–40 is invalid_columns; a total below the sum of the parts
is invalid_total.
Funnels and pyramids
"type": "funnel": stages that narrow as centered bars, or ranked levels as a pyramid. See
Funnels and pyramids.
| Field | Type | Required | Meaning |
|---|---|---|---|
funnel.stages |
array, 2–12 | yes | [{ "label": "Visits", "value": 12400 }], first stage first (at the top). |
funnel.stages[].label |
string, up to 100 characters | yes | The stage; unique. |
funnel.stages[].value |
number above 0 | funnel: yes; pyramid: all or none | Its amount. |
funnel.display |
"funnel" or "pyramid" |
no | Default funnel: bars as wide as the values, with the rate between two stages. pyramid: levels of a triangle, the widest at the bottom. |
Rules: labels are unique (duplicate_label); a value is above zero, on every stage of a funnel and
on all or none of the levels of a pyramid (invalid_value); fewer than 2 stages is empty_data,
more than 12 is too_many_data_points. A funnel whose stage is larger than the one before it is
drawn and reported with the warning stage_exceeds_previous.
Radar charts
"type": "radar": one axis per measure, one closed polygon per series. See
Radar charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
radar.axes |
array, 3–16 | yes | [{ "label": "Speed", "max": 100 }] |
radar.axes[].label |
string, up to 100 characters | yes | The axis; unique. |
radar.axes[].min |
number | no | The value at the center; default 0. |
radar.axes[].max |
number | no | The value at the end of the axis; default the next step of four grid rings above the largest value. Must be greater than min. |
radar.series |
array, 1–4 | yes | [{ "name": "Engine A", "values": [92, 58, 74] }] |
radar.series[].name |
string, up to 100 characters | yes | The series; unique. |
radar.series[].values |
array of numbers | yes | One value for every axis, in the order of axes, within the range of its axis. |
Rules: fewer than 3 axes or no series is empty_data, more than 16 axes too_many_data_points,
more than 4 series too_many_series; labels and names are unique (duplicate_label,
duplicate_series); a series has one value per axis (series_length_mismatch) inside the range of
its axis (value_outside_axis); max above min (invalid_axis_range). More than 8 axes or more
than 3 series is drawn and reported with the warning dense_chart.
Polar charts
"type": "polar": one value per category of a cycle, as wedges or lollipops around a center. See
Polar charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
polar.points |
array, 3–60 | yes | [{ "label": "Jan", "value": 52 }], the first at the top, the others clockwise. |
polar.points[].label |
string, up to 100 characters | yes | The category; unique. |
polar.points[].value |
number, 0 or more | yes | Its value; zero is the center. |
polar.display |
"bars" or "lollipops" |
no | Default bars. |
polar.max |
number above 0 | no | The value at the outer ring; default the next step of four grid rings above the largest value. |
polar.valueLabel |
string | no | What the values are, named in the table; default the word for a value. |
Rules: fewer than 3 points is empty_data, more than 60 too_many_data_points; labels are unique
(duplicate_label); a value is not negative (invalid_value) and not above max
(value_outside_axis); max is above zero (invalid_axis_range). Category names that do not fit
around the chart are left out and reported with the warning labels_omitted.
Mosaic charts
"type": "mosaic": columns as wide as their share of the whole, split by the share of their
segments. See Mosaic charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
mosaic.columns |
array, 1–12 | yes | [{ "label": "Europe", "segments": [{ "label": "Hardware", "value": 120 }] }] |
mosaic.columns[].label |
string, up to 100 characters | yes | The column; unique. |
mosaic.columns[].segments |
array | yes | At least one segment per column. |
mosaic.columns[].segments[].label |
string, up to 100 characters | yes | The segment; unique within its column. Segments of one label share a color across columns. |
mosaic.columns[].segments[].value |
number above 0 | yes | Its amount. |
Rules: no column is empty_data, more than 12 too_many_data_points; a column without segments is
empty_data; labels are unique within their level (duplicate_label); values are above zero
(invalid_value); more than 4 different segment labels in all is too_many_series.
Heatmaps
"type": "heatmap": values, or categories, in a grid of rows and columns. See
Heatmaps.
| Field | Type | Required | Meaning |
|---|---|---|---|
heatmap.rows |
array of strings, 1–60 | yes | The rows; unique labels. |
heatmap.columns |
array of strings, 1–60 | yes | The columns; unique labels. |
heatmap.cells |
array | yes | [{ "row": "Mon", "column": "AM", "value": 42 }]; a cell that is not listed has no value. With categories, category takes the place of value. |
heatmap.categories |
array of strings, up to 4 | no | Makes the heatmap categorical: each category has a palette color and a symbol. |
heatmap.scale |
"sequential" or "diverging" |
no | Default sequential: six steps from the smallest value to the largest. diverging has seven, parted at the midpoint. |
heatmap.midpoint |
number | no | Where a diverging scale turns; default 0. |
heatmap.valueLabel |
string | no | What the values are, named in the key and the table; default the word for a value. |
heatmap.rowOrder, heatmap.columnOrder |
"listed", "label" or "value" |
no | The order of the rows or columns: as listed (default), by label, or by the mean of their values, the largest first. |
Rules: a cell names a row and a column that exist (unknown_row, unknown_column) once
(duplicate_cell), and holds a value or, with categories, a category of the list
(invalid_cell, unknown_category); labels are unique (duplicate_label); up to 4 categories
(too_many_series); a categorical heatmap takes no scale, midpoint or valueLabel
(option_not_supported) and no ordering by value (invalid_order); a midpoint belongs to a
diverging scale.
Waterfalls
"type": "waterfall": a running total that rises and falls step by step. orientation and
Value axis apply. See Waterfalls.
| Field | Type | Required | Meaning |
|---|---|---|---|
waterfall.steps |
array, 2–40 | yes | [{ "label": "Revenue", "value": 1250, "kind": "start" }, { "label": "Costs", "value": -420 }, { "label": "Margin", "kind": "total" }]. |
waterfall.steps[].label |
string, up to 100 characters | yes | The step; unique. |
waterfall.steps[].value |
number | depends | The change, or the start. |
waterfall.steps[].kind |
delta, start, total |
no | A delta (default) adds its value, a start sets the total as a bar from zero, a total shows the running total and takes no value, or the matching one. |
Rules: 2–40 steps (too_many_data_points) with unique labels (duplicate_label); a delta or
start without value is missing_value; a total with a value other than the running total is
total_mismatch.
Fishbone diagrams
"type": "fishbone": the causes of one effect on bones. See
Fishbone diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
fishbone.effect |
string, up to 100 characters | yes | The effect at the head of the spine. |
fishbone.bones |
array, 1–6 | yes | The categories of causes; they alternate above and below the spine. |
fishbone.bones[].label |
string, up to 100 characters | yes | The category; unique. |
fishbone.bones[].causes |
array, up to 8 | no | The causes on the bone. |
fishbone.bones[].causes[].label |
string, up to 100 characters | yes | The cause. |
fishbone.bones[].causes[].causes |
array, up to 4 | no | Sub-causes, [{ "label": "No route training" }]; the last level. |
Rules: 1–6 bones, up to 8 causes per bone and 4 sub-causes per cause (too_many_nodes); bone
labels are unique (duplicate_label); a cause below a sub-cause is not part of the specification
and is refused as an unknown field. The canvas grows for the bones with a canvas_too_small
warning.
Kanban boards
"type": "kanban": columns of cards. See Kanban boards.
| Field | Type | Required | Meaning |
|---|---|---|---|
kanban.columns |
array, 1–6 | yes | The states, left to right. |
kanban.columns[].label |
string, up to 60 characters | yes | The state; unique. |
kanban.columns[].limit |
integer, 1 or more | no | The most cards the column should hold; written as 3/4, marked when exceeded. |
kanban.columns[].cards |
array, up to 20 | no | The cards, top to bottom. |
kanban.columns[].cards[].title |
string, up to 100 characters | yes | The card. |
kanban.columns[].cards[].owner |
string, up to 40 characters | no | Who holds it. |
kanban.columns[].cards[].tag |
string, up to 30 characters | no | A kind of work, drawn as a chip. |
kanban.columns[].cards[].blocked |
boolean | no | Default false; drawn with a sign and the word. |
Rules: 1–6 columns and up to 20 cards per column (too_many_nodes); column labels are unique
(duplicate_label); a limit of 0 is invalid_limit. A column over its limit is drawn, not
refused. The canvas grows for long boards with a canvas_too_small warning.
Story maps
"type": "storymap": activities, tasks and stories in release slices. See
Story maps.
| Field | Type | Required | Meaning |
|---|---|---|---|
storymap.releases |
array of strings, 1–5 | yes | The slices from the top down, such as ["MVP", "Release 2"]; unique. |
storymap.activities |
array, 1–8 | yes | The backbone, left to right; unique labels. |
storymap.activities[].label |
string, up to 100 characters | yes | The activity. |
storymap.activities[].tasks |
array, at least 1 | yes | The user tasks under it; 16 in all. |
storymap.activities[].tasks[].label |
string, up to 100 characters | yes | The task. |
storymap.activities[].tasks[].stories |
array, up to 12 | no | [{ "title": "Search by name", "release": "MVP" }]. |
Rules: up to 5 releases, 8 activities and 16 tasks (too_many_nodes); an activity needs a task
(empty_data); release names and activity labels are unique (duplicate_label); a story names a
release of the map (unknown_release).
User journeys and service blueprints
"type": "journey": stages with rows of actions, touchpoints, thoughts and sentiment. See
User journeys and service blueprints.
| Field | Type | Required | Meaning |
|---|---|---|---|
journey.stages |
array, 2–8 | yes | The stages, left to right. |
journey.stages[].label |
string, up to 60 characters | yes | The stage; unique. |
journey.stages[].action, touchpoint, thought |
string, up to 160 characters | no | What the customer does, where, and thinks. |
journey.stages[].sentiment |
integer, -2 to 2 | no | How the customer feels, drawn as a line with the value written. |
journey.display |
"journey" or "blueprint" |
no | Default journey; blueprint allows the three rows below and draws the lines of interaction and visibility. |
journey.stages[].frontstage, backstage, support |
string, up to 160 characters | no | What the service does where the customer sees it, out of sight, and the systems under it. Blueprint only. |
Rules: 2–8 stages (empty_data, too_many_nodes) with unique labels (duplicate_label); a
sentiment outside -2…2 is invalid_value; frontstage, backstage and support without
"display": "blueprint" are option_not_supported. The canvas grows for long texts with a
canvas_too_small warning.
Value chain maps
"type": "valuemap": components by visibility and evolution. See
Value chain maps.
| Field | Type | Required | Meaning |
|---|---|---|---|
valuemap.components |
array, 1–40 | yes | The components of the map. |
valuemap.components[].id |
string, up to 60 characters | yes | Identifies it in dependencies; unique. |
valuemap.components[].label |
string, up to 100 characters | yes | The name drawn beside the dot; unique. |
valuemap.components[].visibility |
number, 0–1 | yes | 1 is the user’s need itself, 0 is hidden; drawn up. |
valuemap.components[].evolution |
number, 0–1 | yes | 0 is genesis, 1 commodity; drawn right. Stages begin at 0, 0.17, 0.4 and 0.7. |
valuemap.components[].movesTo |
object | no | { "evolution": 0.6, "visibility": 0.5 }; a dashed arrow to the place; visibility defaults to the component’s own. |
valuemap.components[].inertia |
boolean | no | Default false; a bar beside the dot. |
valuemap.dependencies |
array, up to 80 | no | [{ "from": "shop", "to": "hosting" }]: from needs to. |
Rules: 1–40 components (too_many_nodes) with unique ids (duplicate_id) and labels
(duplicate_label); positions between 0 and 1 and a movement to more evolution
(invalid_position); dependencies name components (unknown_node), none to itself
(self_loop) and each once (duplicate_edge).
Venn diagrams
"type": "venn": two or three sets and the regions where they overlap. See
Venn diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
venn.sets |
array of strings, 2–3 | yes | The names of the sets; unique, up to 60 characters. |
venn.regions |
array, up to 7 | no | [{ "sets": ["Design", "Code"], "label": "Prototyping", "count": 4 }]; the region that lies in exactly the sets it names. |
venn.regions[].sets |
array of set names | yes | One to three of venn.sets, each once. |
venn.regions[].label |
string, up to 100 characters | no | Written in the region, or beside the diagram on a leader line. |
venn.regions[].count |
number, 0 or more | no | Written under the label; formatted like other numbers (locale). |
Rules: fewer than two sets is not_enough_sets, more than three too_many_sets; set names are
unique (duplicate_label); a region names sets that exist (unknown_set) and is given once
(duplicate_region); a negative count is invalid_value. The table lists every region, also
those that are not given.
Diagram fields
Sequence, flow, state, class, architecture, tree and network diagrams set their orientation inside
their block; the top-level orientation is refused (see Common fields).
An identifier — id, and every field that names one — is a letter followed by up to 63 letters,
digits, hyphens or underscores (invalid_id).
| Field | Type | Diagrams | Meaning |
|---|---|---|---|
orientation |
auto, portrait, landscape |
sequence, flow, state, classes, architecture, tree | auto (default) takes portrait where that fits the canvas, landscape where only that fits, and otherwise the one that grows the canvas less. |
spacing |
compact, normal, roomy |
flow, state, classes, architecture | compact, normal (default) or roomy scales the gaps between layers and between neighbours in a layer — never steps, texts, margins or the distance between parallel lines; see Spacing. |
mainPath |
array of ids, or { "from", "to" } |
flow, state (array only), architecture | The usual way through the diagram, as ids joined by edges, or the shortest way along the edges between from and to (at a tie the first in the order of the edges). Kept in line and drawn stronger. |
sameRank |
array of id sets | flow, architecture | Sets of at least two ids that lie at one position along the flow; a layout hint that adds no edge. |
fromSide, toSide |
start, end, left, right |
flow edges, architecture connections | Pin the side of a node an edge leaves and reaches, named after the flow so that they hold in either orientation: start faces the nodes before, end the nodes after, left and right lie across the flow (left and right in portrait, top and bottom in landscape); see Port sides. An edge with a side never joins a shared trunk. |
notes |
array, up to 12 | flow, classes, architecture | [{ "on": "a", "text": "…" }, { "text": "…" }]. |
notes[].on |
identifier | — | The step, class, package, component or boundary the note explains; flow charts take notes on steps only. |
notes[].text |
string, up to 160 characters | — | Required; wraps onto as many lines as it needs. |
role |
critical, planned, deprecated |
flow, state, classes, architecture, tree, network, cycle, layers | What a node or an edge means beyond its kind, drawn alike in every diagram. |
emphasis |
object, top level | flow, state, classes, architecture, tree, network, sequence | What the reader should look at first: { "nodes": ["checkout"], "path": ["web", "api", "checkout"] }. |
emphasis.nodes |
array of ids | — | The emphasized nodes (steps, states, classes, components, participants), each named once; they get the accent outline and tint. More than two warn with too_much_emphasis at /emphasis/nodes. A composite state is a frame and cannot be named. |
emphasis.path |
array of ids, at least two | — | The nodes along the emphasized path, each joined to the next by an edge (a message in a sequence diagram), in either direction; the edges between them get the accent line. |
legend |
auto, none |
flow, state, classes, architecture, tree, network, sequence | auto draws a strip below the drawing; none (the default) draws none. See below. |
Notes. A note on an element is drawn beside it and joined to it by a dotted line that is no
edge, relation or connection; a note on a package or boundary stands inside that frame, at the
head of its contents, without a line; a note without on explains the whole diagram and stands in
a band below it, notes side by side and wrapping onto further rows. The description gives every
note (Note on Card: …, Note: …); the table gives notes on elements in a Notes column of their
rows and the others, which belong to no row, after its caption. More than 12 notes are
too_many_notes, a longer text text_too_long, an on that names nothing unknown_node at
/…/notes/0/on.
Roles. On a step, component, state, node or class, and on an edge, connection, transition or
relation. critical: a 3 px red outline or a 2.6 px red line and a badge with !; planned:
dashed and violet, badge +; deprecated: dotted and gray, badge −. The badge sits on the top
left corner of a box. Colors are the theme’s role colors (--chartlet-role-red, -violet,
-gray), so light, dark, blueprint and print each resolve them. A role keeps the shape and fill of
the kind (and a value’s fill); it overrides the color of the main path, which keeps its place in
the layout, while a weight keeps its line width; planned and deprecated have a pattern of
their own, so an edge, connection or transition with one of them takes no dash
(role_conflict); critical keeps a dash. A legend below the diagram names only the roles used;
the description lists what has each role, and the table adds a Role column when a step or
component has one and names the role of an edge in its notes. A state diagram’s table, one row per
transition, adds a Role column for transitions and names a state with its role,
Locked (critical); a tree’s and a network’s table add a Role column for nodes, a class diagram’s
for classes, and name the role of a relation or a network edge in its notes. A network node is a
dot too small for a badge: a ring around it in the role’s color and pattern says the role, and the
dot keeps the color of its group. A composite state is a frame and takes no role
(option_not_supported). The marks at the ends of a relation keep their form, a hollow mark stays
hollow; a realization or a dependency is dashed by its kind, so it takes critical only
(role_conflict at its role). An unknown role is invalid_spec.
Emphasis. emphasis marks what to look at first, without adding anything to the diagram. An
emphasized node keeps its shape and gets a 3.5 px outline in the accent color, heavier than any
other outline, and the accent at 14 % opacity as its fill; the edges (in a sequence diagram, the
messages) between consecutive nodes of path get a 3.2 px accent line and arrowhead, and their
labels turn bold. A network node is a dot too small for an outline of its own: a ring in the accent
color stands around it. Everything else stays neutral, so the cue is weight as well as color, and
the accent meets the contrast the theme’s accent has against the ground in light, dark,
blueprint and print. Without path, a mainPath stays drawn in the accent; with one, the
emphasized path is the accent line and the main path keeps its place in the layout but is drawn
neutral. The description names the emphasized nodes and the path (Emphasized: Pay. Emphasized path: Cart → Pay → Paid?.) and the table adds an Emphasis column (emphasized, on emphasized path; in a sequence diagram the messages between the path’s participants). Errors: an id that
names nothing is unknown_node at /emphasis/nodes/0 or /emphasis/path/0; a node named twice
duplicate_emphasis; a path of one node emphasis_path_short; two consecutive nodes of the path
that no edge joins emphasis_path_gap at the second; emphasis on another chart type
option_not_supported at /emphasis.
Legend strip. "legend": "auto" draws one horizontal strip below the drawing, in rows that
fit the canvas, and grows the canvas by its height, so it never lies inside the drawing. It lists
only what occurs: a sample and the name of each node kind (flow, state, architecture, tree), each
line pattern in use (solid, dashed, dotted; in a sequence diagram the call, reply and
asynchronous message), the main path or the emphasized path in the accent, and an emphasized
node. It is drawn when the diagram uses more than one node kind or line pattern, or emphasizes
something; otherwise there is nothing to explain. With "spacing": "compact" the strip is left
out and the description says so (The legend is left out in compact mode.); otherwise the
description ends with Legend: … naming the entries. The default is none, so that a diagram
that does not ask for a legend is drawn exactly as before; the roles keep their own legend below
the diagram. A time chart takes top and end for its series (option_not_supported the other
way round).
Layout. A sequence diagram or flow chart — and every diagram laid out like one — that does not
fit its canvas is drawn larger and reported as canvas_too_small at /width or /height. A
fromSide or toSide along the flow that would turn the edge back over its step, such as start
on an edge that leads on to a later step, is ignored and reported as port_side_ignored at that
field (flow charts and architecture diagrams); an edge from a node to itself takes no fromSide
or toSide (option_not_supported). A main path must follow edges (main_path_gap) or, given
as { "from": "a", "to": "b" }, have a way along them (main_path_unreachable). In a sameRank
set, each id appears in one set at most, and no two of a set are joined by an edge
(same_rank_edge).
Sequence diagrams
"type": "sequence". See Sequence diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
sequence.participants |
array, 1–12 | yes | [{ "id": "api", "label": "…", "sublabel": "…", "kind": "service" }]. |
participants[].id |
identifier | yes | Unique. |
participants[].label, participants[].sublabel |
string, up to 60 characters | label | Name and second line. |
participants[].kind |
service, actor, database, queue, external |
no | Each kind has its own shape; default service. |
sequence.messages |
array, 1–60 | yes | [{ "from": "api", "to": "db", "label": "…", "kind": "call" }], in the order they are sent. |
messages[].from, messages[].to |
identifier | yes | Sender and receiver; a participant may send to itself. |
messages[].label |
string, up to 80 characters | yes | The message. |
messages[].kind |
call, reply, async |
no | Default call; a call activates its receiver until the receiver replies. |
sequence.fragments |
array, up to 12 | no | [{ "kind": "alt", "label": "…", "from": 0, "to": 2, "else": [{ "from": 1, "label": "…" }] }]: frames around runs of consecutive messages. |
fragments[].kind |
alt, opt, loop, par, critical, break |
yes | The kind of frame. |
fragments[].from, fragments[].to |
integer, from 0 | yes | The first and last message inside, counted from 0 and included. |
fragments[].label |
string, up to 60 characters | no | Its condition. |
fragments[].else |
array | no | Only alt: further branches, each starting at a later message inside the frame (from) with an optional label. |
sequence.numbered |
boolean | no | Numbers the messages in round badges at the start of their arrows. |
sequence.orientation |
auto, portrait, landscape |
no | See Diagram fields. |
Rules: 1–12 participants (too_many_participants), 1–60 messages (too_many_messages) and up to
12 fragments (too_many_fragments); participant ids are identifiers (invalid_id) and unique
(duplicate_id), every message names existing participants (unknown_participant), and
fragments lie within the messages, nest at most three deep and never cross (invalid_fragment,
fragments_cross).
Flow charts
"type": "flow": steps laid out in layers along the flow, down in portrait and right in landscape.
Edges may form cycles; an edge back is drawn against the flow. See
Flow charts.
| Field | Type | Required | Meaning |
|---|---|---|---|
flow.nodes |
array, 1–40 | yes | The steps: [{ "id": "a", "label": "…", "sublabel": "…", "tag": "DB", "kind": "process", "lane": "x", "value": 3, "parts": [{ "label": "…", "value": 2 }], "role": "critical" }]. |
nodes[].id |
identifier | yes | Unique among steps and lanes. |
nodes[].label, nodes[].sublabel |
string, up to 60 characters | label | Name and second line. |
nodes[].tag |
string, up to 8 characters | no | The type tag of the step in the editorial look, replacing the tag of its kind; text_too_long at /flow/nodes/<i>/tag. Architecture components take tag too. |
nodes[].kind |
process, start, end, decision, io, subprocess, store, external |
no | Each kind has its own shape; default process. |
nodes[].lane |
identifier | with lanes | The lane the step lies in. |
nodes[].value |
number | no | Colors the step’s fill, from light for the smallest value to dark for the largest. |
nodes[].parts |
array, up to 6 | no | [{ "label", "value" }] (label up to 40 characters, value 0 or more): the parts of what the step holds, drawn as a stripe inside the bottom of its box. |
nodes[].role |
role | no | See Diagram fields. |
flow.edges |
array, up to 80 | no | [{ "from": "a", "to": "b", "label": "…", "dash": "solid", "weight": 120, "role": "planned", "fromSide": "right", "toSide": "start" }]. |
edges[].from, edges[].to |
identifier | yes | The steps it joins. |
edges[].label |
string, up to 60 characters | no | Its text. |
edges[].dash |
solid, dashed, dotted |
no | Default solid. |
edges[].weight |
number, 0 or more | no | How much the edge carries: the line is drawn from 1 px for nothing to 5 px for the heaviest edge. |
edges[].role, edges[].fromSide, edges[].toSide |
no | See Diagram fields. | |
flow.lanes |
array, up to 8 | no | [{ "id": "x", "label": "…" }] (label up to 40 characters): bands across the flow; once there are lanes, every step names one. |
flow.groups |
array, up to 8 | no | [{ "label": "…", "nodes": ["a"] }] (label up to 40 characters): frames around steps of one lane. |
flow.mainPath, flow.sameRank, flow.notes, flow.orientation, flow.spacing |
no | See Diagram fields. | |
flow.valueLabel |
string, up to 40 characters | no | What the value of the steps stands for: the name of the color key and of the table column. |
flow.sharedTrunks |
boolean | no | Draws edges that leave a step together, or reach one together, on one line that branches, where they read the same (no label, same dash, weight and role, off the main path, other ends in the same lane and group, not out of a decision; note lines never). Every edge is still drawn and listed. |
Rules: 1–40 steps (too_many_nodes), up to 80 edges (too_many_edges), 8 lanes
(too_many_lanes) and 8 groups (too_many_groups); step and lane ids are identifiers
(invalid_id) and unique (duplicate_id); edges, groups and the main path name existing steps
(unknown_node); every step names a declared lane once there are lanes (missing_lane,
unknown_lane); a step belongs to one group at most (duplicate_member) and a group to one lane
(group_spans_lanes); a weight is at least 0 (invalid_edge_weight); parts (flow charts and
trees) has at most 6 entries per node and 8 names per diagram (too_many_parts) with values of
at least 0 that add up to more than 0 (invalid_value) and stays off decisions
(option_not_supported). A step that lands inside the frame of a group it does not belong to is
group_overlap. The rules in Diagram fields apply.
State diagrams
"type": "state": laid out like a flow chart. Transitions are written event [guard] / action.
See State diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
state.states |
array, 1–40 | yes | [{ "id": "a", "label": "…", "sublabel": "…", "kind": "state", "final": false, "in": "c", "role": "critical" }]. |
states[].id |
identifier | yes | Unique. |
states[].label, states[].sublabel |
string, up to 60 characters | label | Name and second line. |
states[].kind |
state, choice, composite |
no | state (default); a choice is a diamond; a composite is a frame around the states that name it with in. |
states[].final |
boolean | no | A final state, drawn with a double outline. |
states[].in |
identifier | no | The composite state it lies in. |
states[].role |
role | no | Not on a composite state. |
state.transitions |
array, up to 80 | no | [{ "from": "a", "to": "b", "event": "…", "guard": "…", "action": "…", "dash": "solid", "role": "planned" }]. |
transitions[].from, transitions[].to |
identifier | yes | The states it joins. |
transitions[].event, transitions[].guard, transitions[].action |
string, up to 40 characters | no | The parts of its label. |
transitions[].dash |
solid, dashed, dotted |
no | Default solid. |
transitions[].role |
role | no | See Diagram fields. |
state.initial |
identifier | no | The state the machine starts in: a dot with an arrow into it. |
state.mainPath |
array of ids | no | The usual way through the states, joined by transitions. |
state.orientation, state.spacing |
no | See Diagram fields. |
Rules: 1–40 states (too_many_states) and up to 80 transitions (too_many_transitions); ids are
identifiers (invalid_id) and unique (duplicate_id); transitions, initial and the main path
name existing states (unknown_state) and follow transitions (main_path_gap); a choice or
composite state cannot be final (option_not_supported); states lie in composite states
(not_composite) without a circle (state_cycle) at most four deep (states_too_deep), a
composite state holds a state (empty_composite) and is never named by a transition, initial or
the main path (composite_not_allowed); with an initial state every other state needs a
transition into it (unreachable_state, a warning).
Class diagrams
"type": "classes": a class or entity-relationship diagram, laid out like a flow chart. See
Class and entity diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
classes.classes |
array, 1–40 | yes | [{ "id": "order", "label": "Order", "kind": "class", "attributes": ["+ id: Int"], "methods": ["+ total(): Money"], "role": "critical" }]. |
classes[].id |
identifier | yes | Unique among classes and packages. |
classes[].label |
string, up to 60 characters | yes | The name. |
classes[].kind |
class, interface, abstract, enum, entity |
no | class (default), interface, abstract, enum (a stereotype above the name) or entity (an entity of an entity-relationship diagram). |
classes[].attributes, classes[].methods |
array of strings, up to 30, each up to 100 characters | no | Lines of text in their own compartments. |
classes[].in |
identifier | no | The package the class lies in; classes outside every package are allowed. |
classes[].role |
role | no | See Diagram fields. |
classes.relations |
array, up to 100 | no | [{ "from": "order", "to": "line", "kind": "composition", "fromCardinality": "1", "toCardinality": "1..*", "label": "has", "role": "planned" }]. |
relations[].from, relations[].to |
identifier | yes | The classes it joins; relations join classes, never packages. |
relations[].kind |
association, inheritance, realization, composition, aggregation, dependency |
no | association (default, a plain line), inheritance (hollow triangle at to), realization (dashed, hollow triangle at to), composition (filled diamond at from), aggregation (hollow diamond at from) or dependency (dashed, open arrowhead at to). |
relations[].label |
string, up to 60 characters | no | Its text. |
relations[].fromCardinality, relations[].toCardinality |
string, up to 12 characters | no | Stand at the ends, such as 0..*. |
relations[].direction |
forward, both, none |
no | Associations only: forward (navigable to to, an open arrowhead there), both (open arrowheads at either end) or none (a plain line). |
relations[].role |
role | no | See Diagram fields. |
classes.packages |
array, up to 12 | no | [{ "id": "ordering", "label": "Ordering", "in": "shop", "orientation": "landscape" }]: frames that group classes into modules. |
packages[].id |
identifier | yes | Unique among classes and packages. |
packages[].label |
string, up to 60 characters | yes | The name. |
packages[].in |
identifier | no | The package it lies in. |
packages[].orientation |
portrait, landscape |
no | Lays out the package’s classes that way as one block, whichever way the diagram runs; relations from outside run on straight to their class. |
classes.notes, classes.orientation, classes.spacing |
no | See Diagram fields. |
The description names every package with what it holds, and the table gains a Package column
(Shop › Ordering).
Rules: up to 40 classes (too_many_nodes) with unique ids (duplicate_id, invalid_id), at most
30 attributes and 30 methods each (too_many_data_points), and 100 relations (too_many_edges)
between existing classes (unknown_node, also for a package); direction on another kind than an
association is option_not_supported. Packages: up to 12 (too_many_packages) nested at most
four deep (packages_too_deep), classes and packages lie in existing packages
(unknown_package) without a circle (package_cycle), and every package holds a class
(empty_package). auto as a package’s orientation, and a package with one inside another with
one, are option_not_supported; a package whose block a relation from outside would have to cross
follows the diagram (package_orientation_ignored, a warning).
Architecture diagrams
"type": "architecture": components joined by connections inside boundaries, laid out like a flow
chart. See Architecture diagrams.
| Field | Type | Required | Meaning |
|---|---|---|---|
architecture.components |
array, 1–40 | yes | [{ "id": "a", "label": "…", "sublabel": "…", "kind": "service", "in": "net", "role": "critical", "tags": ["public"], "provides": [{ "id": "api", "label": "…" }] }]. |
components[].id |
identifier | yes | Unique among components and boundaries. |
components[].label, components[].sublabel |
string, up to 60 characters | label | Name and second line, such as its technology or host. |
components[].kind |
person, frontend, service, database, queue, storage, cache, security, external, node, artifact |
no | Each with its own shape; default service. node and artifact are for deployments. |
components[].symbol |
browser, mobile, api-gateway, load-balancer, cdn, firewall, identity, container, orchestrator, function, cloud, cache, object-storage, message-broker, search-index, monitoring |
no | Draws a generic symbol above the label and names a technology category in the description and the data table, after the kind; the kind keeps its shape. |
components[].in |
identifier | no | The boundary it lies in. |
components[].role |
role | no | See Diagram fields. |
components[].tags |
array of identifiers, up to 8 | no | Names of groups the component belongs to, such as public, by which views include or exclude it; not drawn. |
components[].provides |
array, up to 4 | no | Interfaces the component provides, each a ball on a stem ahead of its box with the label beside it. Not on person or node. |
provides[].id |
identifier | yes | Unique among the interfaces of the component; with the component’s id it forms the stable id component.interface. |
provides[].label |
string, up to 40 characters | yes | Its name. |
architecture.connections |
array, up to 80 | no | [{ "id": "c1", "from": "a", "to": "b", "label": "…", "technology": "…", "dash": "solid", "weight": 120, "role": "planned", "direction": "forward", "toInterface": "api", "fromSide": "left", "toSide": "start", "tags": ["sync"] }]. |
connections[].id |
identifier | no | A stable name by which views and perspectives select the connection; unique among connections. |
connections[].from, connections[].to |
identifier | yes | The components it joins. |
connections[].label |
string, up to 60 characters | no | What it does. |
connections[].technology |
string, up to 40 characters | no | How, written in brackets on a line below the label. |
connections[].step |
integer, 1–99 | no | Numbers the connection in a sequence of calls; the number is written before the label, and the description lists the steps in order. |
connections[].dash |
solid, dashed, dotted |
no | Default solid. |
connections[].weight |
number, 0 or more | no | How much it carries: the line is drawn from 1 px for nothing to 5 px for the heaviest. |
connections[].direction |
forward, both, none |
no | forward (default, an arrowhead at to), both (one connection both ways, arrowheads at either end), none (no direction, no arrowhead). |
connections[].toInterface |
identifier | no | An interface to provides, which from requires: the connection ends in a socket around the ball, runs forward, takes no toSide and never joins a shared trunk. |
connections[].tags |
array of identifiers, up to 8 | no | Groups the connection belongs to, by which views include or exclude it; not drawn. |
connections[].role, connections[].fromSide, connections[].toSide |
no | See Diagram fields. | |
architecture.delta |
object | no | { "before": "today", "after": "target" }: the keys of two views drawn side by side as before and after, with a ledger of what was added, removed, changed, moved and rewired. See Architecture delta. unknown_view, same_view, option_not_supported. |
components[].replaces |
identifier | no | In a delta, the component this one stands in for in the state after (unknown_component). |
architecture.boundaries |
array, up to 12 | no | [{ "id": "net", "label": "…", "in": "region" }] (label up to 60 characters): frames such as a cloud region, a network or a system. |
architecture.mainPath, architecture.sameRank, architecture.notes, architecture.orientation, architecture.spacing |
no | See Diagram fields, with components and connections. | |
architecture.sharedTrunks |
boolean | no | Draws connections that leave a component together, or reach one together, on one line that branches, where they read the same (no label, technology or step, usual direction, same dash, weight and role, off the main path, other ends in the same boundary). |
architecture.views |
array, up to 20 | no | Views on the model; see Views. |
architecture.defaultView |
identifier | no | The key of the view rendered when none is asked for; without it, the first view. |
architecture.perspectives |
array, up to 8 | no | See Perspectives. |
architecture.perspective |
identifier | no | The key of the perspective the diagram shows, and every view without one of its own. |
Rules: 1–40 components (too_many_nodes), up to 80 connections (too_many_edges) and 12
boundaries (too_many_boundaries) nested at most four deep (boundaries_too_deep); identifiers
are unique among components and boundaries (duplicate_id), components and boundaries lie in
existing boundaries (unknown_boundary) without a circle (boundary_cycle), every boundary holds
a component (empty_boundary); connections and the main path name existing components
(unknown_node, main_path_gap, main_path_unreachable); connection ids are unique among
connections (duplicate_id); a symbol is one of the names above (unknown_symbol); a step lies
from 1 to 99 (invalid_step) and every number is used once (duplicate_step). Tags: up to 8 per
component or connection (too_many_tags), written like ids (invalid_tag), each once
(duplicate_tag). Interfaces: at most four per component (too_many_interfaces), ids unique
within it (duplicate_id), none on a person or node, and a connection to an interface runs
forward and takes no toSide (option_not_supported); a toInterface that to does not provide
is unknown_interface. A component that lands inside the frame of a boundary it does not belong to
is group_overlap. The rules in Diagram fields apply.
Views
With views, one view is rendered — the one asked for, else defaultView, else the first — as a
diagram of its own, never the whole model. See
Views on one model.
| Field | Type | Required | Meaning |
|---|---|---|---|
views[].key |
identifier | yes | A stable name of the view; unique among views. |
views[].title |
string, up to 200 characters | yes | The title of the diagram the view renders. |
views[].description |
string, up to 1000 characters | no | Its description; without one it is generated from what the view shows. |
views[].components |
array of ids | no | The components the view shows. Required unless the view has steps, include or exclude. |
views[].connections |
array of ids | no | The connections it shows, each between shown components; without it, every connection between them. |
views[].mainPath |
array of ids, or { "from", "to" } |
no | The way the view draws stronger; without one, the model’s main path when it lies in the view. |
views[].include |
array of tags | no | Shows every component carrying one of these tags and none of exclude; with connections, also the connections between shown components that carry one. |
views[].exclude |
array of tags | no | Leaves out components and connections carrying one, unless the view lists them. With neither components nor include, the view starts from every component. |
views[].steps |
array, up to 99 | no | A dynamic view: [{ "connection": "c1", "label": "…" }], connections of the model in the order they occur, numbered from 1 in place of their own steps: connection names one by id, the same may occur again, and label (up to 60 characters) replaces the connection’s. |
views[].perspective |
identifier | no | The perspective the view shows, in place of the model’s. |
views[].width, views[].height, views[].mobile |
no | Take the place of the model’s and are checked like them, at the view (invalid_dimension at /architecture/views/0/width). |
|
views[].environment |
string, up to 60 characters | no | Makes the view a deployment view of this environment, such as Production. |
views[].hosts |
array | deployment | Machines, clusters or zones, nested like boundaries (id, label, in). |
views[].instances |
array | deployment | [{ "id": "a-1", "of": "a", "in": "node" }]: running instances of the model’s components, drawn as Label (id). id is unique among the instances and hosts of the view, of names the component, in the host. |
views[].instanceConnections |
array | no | Connections between instances, with the fields of a connection and from/to naming instances. One with the id of a model connection takes the label, technology, dash, role, fromSide and toSide it leaves out from it. |
views[].deriveConnections |
boolean | no | Deployment views only: adds an instance connection for every model connection the view does not name by id, from each instance of its from component to the nearest instances of its to component by shared hosts, all of them at a tie; see Environments and instances. |
Rules: up to 20 views (too_many_views) with unique keys (duplicate_id); a view lists
components or steps (empty_view) that exist (unknown_node, unknown_connection), each listed
connection or step joins shown components (connection_outside_view), a view with steps takes
no connections (option_not_supported) and at most 99 steps (too_many_steps), and
defaultView and the key asked for name a view (unknown_view). include and exclude name tags
the model carries (unknown_tag), each once in both together (duplicate_tag), select at least
one component (empty_view), and are not taken by a view with steps or an environment
(option_not_supported). A deployment view lists at least one instance (empty_view), each of
an existing component (unknown_node) and drawn as Label (id) in at most 60 characters
(text_too_long at the instance’s id), and takes no components, connections, steps or
mainPath; another view takes no hosts, instances, instanceConnections or
deriveConnections (option_not_supported); an instance connection with an id names a model
connection (unknown_connection) between the components its instances run
(instance_mismatch). Instances, hosts and instance connections are then checked like components,
boundaries and connections, after expansion, with diagnostics in the view. Every view must resolve
to a valid diagram; its diagnostics point into the model or the view. A view keeps the notes
without on and those on the components and boundaries it shows; a deployment view keeps only the
notes without on.
Perspectives
perspectives name questions such as the team or the technical debt; the diagram shows one only
when perspective (on the model or a view) selects it. See
Perspectives.
| Field | Type | Required | Meaning |
|---|---|---|---|
perspectives[].key |
identifier | yes | A stable name; unique among perspectives. |
perspectives[].title |
string, up to 40 characters | yes | Its name in the legend, the description and the table, such as Team. |
perspectives[].description |
string, up to 200 characters | no | A sentence added to the description. |
perspectives[].categories |
array, 1–8 | yes | [{ "label": "Payments", "components": ["a"], "connections": ["c1"] }], in the order that fixes their colors. |
categories[].label |
string, up to 40 characters | yes | The category, such as a team; unique in the perspective. |
categories[].components, categories[].connections |
array of ids | no | What lies in the category; connections by id. |
Rules: up to 8 perspectives (too_many_perspectives) with well-formed keys of their own
(invalid_id), each with 1–8 categories (invalid_categories) with distinct labels
(duplicate_category) that list existing components (unknown_node) and connections
(unknown_connection), each in at most one category of a perspective (duplicate_member);
perspective on the model or a view names an existing perspective (unknown_perspective).
Trees
"type": "tree": one root and nodes that each name their parent, or a dendrogram built from
distances. See Trees and
Dendrograms.
| Field | Type | Required | Meaning |
|---|---|---|---|
tree.nodes |
array, 1–150 | one of | [{ "id": "a", "label": "…", "sublabel": "…", "parent": "root", "link": "60 %", "kind": "unit", "partner": "b", "value": 3, "parts": [{ "label": "…", "value": 2 }], "role": "critical" }]. The children of a node keep the order of the list. |
nodes[].id |
identifier | yes | Unique. |
nodes[].label, nodes[].sublabel |
string, up to 60 characters | label | Name and second line. |
nodes[].parent |
identifier | no | The node it hangs from; exactly one node has neither a parent nor a partner: the root. |
nodes[].link |
string, up to 40 characters | no | What joins the node to its parent, such as a share. |
nodes[].kind |
unit, person, external |
no | Default unit. |
nodes[].partner |
identifier | no | Joins two nodes as a couple, side by side; the children of either hang from the middle of the line between them. |
nodes[].collapsed |
boolean | no | Draws the node without what hangs below it (and below its partner), with a +n for the nodes left out; the description and the table still list them. |
nodes[].value |
number | no | Colors the node’s fill, from light for the smallest value to dark for the largest. |
nodes[].parts |
array, up to 6 | no | As on a flow chart step. |
nodes[].role |
role | no | See Diagram fields. |
tree.orientation |
auto, portrait, landscape |
no | See Diagram fields. |
tree.layout |
layered, radial |
no | layered (default): one row per level (a column in landscape). radial draws a mind map: the root in the middle, every level on a ring around it, each subtree in a sector as wide as its share of the leaves. |
tree.valueLabel |
string, up to 40 characters | no | What the value of the nodes stands for: the name of the color key and of the table column. |
tree.distances |
object | one of | For a dendrogram: { "labels": ["a", "b"], "matrix": [[0, 1], [1, 0]] }, the distances between items, from which the tree is built by agglomerative clustering. |
distances.labels |
array of strings, 2–40, each up to 60 characters | yes | The items, in the order of the matrix. |
distances.matrix |
array of arrays of numbers | yes | A square, symmetric matrix with 0 on its diagonal and no value below 0. |
tree.linkage |
average, complete, single |
no | With distances: how far apart two clusters are — the mean distance between their items (average, default), the largest (complete) or the smallest (single). |
Rules: 1–150 nodes (too_many_nodes) down to twelve levels below the root (tree_too_deep);
identifiers are unique (duplicate_id), exactly one node has no parent (invalid_root), parents
exist (unknown_node) and run up to the root without a circle (circular_parent), the root has
no link (option_not_supported), a radial tree has no orientation, partner or link
(option_not_supported), and a partner exists, is another node, has no parent and is named by one
node only (unknown_node, invalid_partner); collapsed on a node with nothing below it warns
with nothing_to_collapse. parts follows the rules of flow charts. A dendrogram takes 2–40
labels (empty_data, too_many_nodes) that are unique (duplicate_id) and a square, symmetric
matrix with 0 on its diagonal and no value below 0 (invalid_matrix), and no nodes, layout,
orientation or valueLabel; linkage without distances is refused (option_not_supported).
Networks
"type": "network": nodes as dots joined by edges, with no direction of flow. See
Networks.
| Field | Type | Required | Meaning |
|---|---|---|---|
network.nodes |
array, 1–40 | yes | [{ "id": "a", "label": "…", "group": "…", "role": "critical" }]. |
nodes[].id |
identifier | yes | Unique. |
nodes[].label |
string, up to 60 characters | yes | The name. |
nodes[].group |
string, up to 40 characters | no | Its color and a legend entry. |
nodes[].role |
role | no | Drawn as a ring around the dot. |
network.edges |
array, up to 120 | no | [{ "from": "a", "to": "b", "dash": "solid", "weight": 3, "role": "planned" }]. |
edges[].from, edges[].to |
identifier | yes | The nodes it joins. |
edges[].dash |
solid, dashed, dotted |
no | Default solid. |
edges[].weight |
number, 0 or more | no | The line is drawn from 1 px for nothing to 5 px for the heaviest edge. |
edges[].role |
role | no | See Diagram fields. |
network.layout |
circle, rings |
no | circle (default): the nodes clockwise from twelve o’clock, groups together; rings around center, every other node on the ring of its distance in edges. |
network.center |
identifier | rings | The node in the middle of the rings. |
network.order |
crossings, listed |
no | Circle only: crossings (default, groups together with few crossings) or listed. |
network.directed |
boolean | no | Edges lead from one node to the other, drawn with an arrowhead. |
Rules: 1–40 nodes (too_many_nodes), up to 120 edges (too_many_edges) and 8 groups
(too_many_groups); identifiers are unique (duplicate_id), edges join two different existing
nodes (unknown_node, self_loop) and carry a weight of at least 0 (invalid_edge_weight);
rings need a center that exists (missing_center, unknown_node) and take no order, and a
circle takes no center (option_not_supported).
Cycles
"type": "cycle": steps on a ring where the last step feeds the first, optionally around a hub.
See Cycles.
| Field | Type | Required | Meaning |
|---|---|---|---|
cycle.steps |
array, 3–8 | yes | [{ "id": "a", "label": "…", "note": "…", "hubLabel": "…", "role": "critical" }], clockwise from twelve o’clock. |
steps[].id |
identifier | yes | Unique. |
steps[].label |
string, up to 60 characters | yes | The name. |
steps[].note |
string, up to 80 characters | no | A second, smaller line. |
steps[].hubLabel |
string, up to 24 characters | no | The label of the link to the hub; needs a hub. |
steps[].role |
role | no | See Diagram fields. |
cycle.hub |
object | no | { "label": "…", "note": "…" } (label up to 60, note up to 80 characters): what the steps feed, in the middle. |
cycle.labels |
outside, inside |
no | outside (default): numbered dots with the labels beside them; inside: boxes that hold the labels. |
Rules: 3 to 8 steps (too_few_steps, too_many_steps); identifiers are unique (duplicate_id);
hubLabel needs a hub (option_not_supported).
Layers
"type": "layers": abstraction levels stacked on each other, or scopes drawn inside each other.
See Layers.
| Field | Type | Required | Meaning |
|---|---|---|---|
layers.layers |
array, 1–10 (6 nested) | yes | [{ "id": "a", "label": "…", "items": ["…"], "note": "…", "role": "critical" }]: from the bottom up in a stack, from the outermost inwards in nested scopes. |
layers[].id |
identifier | yes | Unique. |
layers[].label |
string, up to 60 characters | yes | The name. |
layers[].items |
array of up to 12 strings of up to 40 characters | no | What lies in the layer, drawn as small boxes. |
layers[].note |
string, up to 120 characters | no | A remark at the side of the layer (under its label on a narrow canvas). |
layers[].role |
role | no | See Diagram fields. |
layers.display |
stack, nested |
no | stack (default): bands, the first at the bottom; nested: frames inside each other, the first the outermost. |
layers.projection |
flat, exploded |
no | flat (default); exploded draws a stack as slabs in 2:1 dimetric projection, lifted apart, with the labels on leader lines (a flat stack on a narrow canvas). Not with nested (option_not_supported). |
Rules: at least one layer, at most 10 in a stack and 6 nested scopes (too_many_layers); at most
12 items per layer (too_many_items); identifiers are unique (duplicate_id).
Plans
"type": "plan": rooms, buildings and open ground on a grid, seen at an angle. See
Plans.
| Field | Type | Required | Meaning |
|---|---|---|---|
plan.areas |
array, 1–30 | yes | [{ "id": "a", "label": "…", "kind": "room", "x": 0, "y": 0, "w": 6, "h": 4, "phase": "…", "items": [] }]. |
areas[].id |
identifier | yes | Unique. |
areas[].label |
string, up to 60 characters | yes | The name. |
areas[].kind |
room, building, outdoor |
no | room (default); building has a heavier outline; outdoor has no walls and may lie under other areas. |
areas[].x, areas[].y |
number, 0–100 | yes | The corner of the area, in grid units. |
areas[].w, areas[].h |
number above 0, up to 100 | yes | The width along x and the depth along y. |
areas[].phase |
string, up to 40 characters | no | When the area exists or is built; up to four phases, each with a pattern. |
areas[].items |
array, up to 12 | no | [{ "kind": "desk", "x": 0.5, "y": 0.5 }]; kinds desk, shelf, table, bed, sofa, plant; x and y in grid units from the corner of the area. |
Rules: 1–30 areas (too_many_areas); unique identifiers (duplicate_id); x, y, w and h
numbers in range (invalid_area); no two rooms or buildings overlap (area_overlap); at most four
phases (too_many_phases); an item lies inside its area (item_outside_area, too_many_items).
Diverging scale
Stripes and calendars share one color scale: a neutral middle at reference (default 0) and
eight equally wide steps on either side. min and max set where the outermost steps begin and
default to the largest distance of any value from the reference; values beyond them take the
outermost step. The 17 colors are CSS custom properties --chartlet-diverging-0 to -16 and can
be overridden by the host page.