mayaCharts

Spec reference

Every field of the spec, what it applies to and what it does. Error messages link to the field they name.

Overview

The mayaCharts spec is plain JSON. Rule: x is always the category, y is always the value, whatever the orientation. yDomain is always the value axis.

Where the element reads the spec

The <maya-chart> element takes its spec from the first of these that is set: the spec property (assigning data sets it too), then the JSON script child, then the spec attribute. A property you have set wins over any later change to the attribute, so use one source. Assign undefined to the property to fall back to the script child or the attribute.

Data fields

Field Type Default Meaning
$schema string - Ignored; for editors and LLMs
type enum required bar line area scatter heatmap waterfall kpi dumbbell ridgeline beeswarm parallel table treemap sunburst sankey chord marimekko waffle radial hexmap boxplot funnel weave units orbit constellation Core: ridgeline, beeswarm, parallel, table; Modules: treemap/sunburst/marimekko/waffle (hierarchy), sankey/chord (flow), radial (radial), hexmap (geo), boxplot/funnel (stats), weave (weave), units (units), orbit (orbit), constellation (constellation)
data Row[] required Row objects
aggregate sum / mean / count / min / max sum How rows sharing a (category, series) combine. count = SQL COUNT(y) on non-null rows. Not boxplot, which summarises raw rows
sort asc / desc data order Categories by total across all series (hidden ones too)
limit number - Keep top N categories by signed total; rest roll up into "Other" with re-aggregation. A bar with more categories than fit (past 10000 marks) does this on its own when limit is unset, and says so in warnings

Encoding fields

Field Type Applies to Meaning
x field all but path types Category; scatter numeric x; hexmap state (USPS code or full name, case-insensitive); boxplot one box each; funnel the stage, omitted when y lists the stages
xType auto / category / time bar line area How x is spaced. "auto": time axis when every x is an ISO 8601 date on line, area or vertical bar without sort/limit; else categories. "time" accepts epoch ms. Default auto
y field or field[] all Value; array adds measure toggle, first active (parallel and table: all shown; funnel without x: one stage per field)
y2 field bar orbit Second value field, drawn as a line on a right axis. Vertical bars only. Orbit: growth, which sets each planet's speed and direction
was field bar Previous value field. Each bar gets a dashed ghost bar at its old value drawn over it, keyed in the legend by the field's title, the tooltip says "was" and the value, the data table gains a column, and the description names the 2 largest relative moves (text.since, "Since Last week: North +12%, West -10%."). Not with stack or a y array
forms ("waffle" / "bars" / "swarm")[] units Forms a units chart switches between, first one shown (view.form picks another). With 2 or more the chart shows a form control (text.forms, text.waffle, text.bars, text.swarm). Default all three
frame field bar line area scatter dumbbell Playback: one frame per distinct value of the field, in data order (values match as strings, null rows belong to no frame, at most 200 frames). The chart shows one frame, the last by default (view.frame picks another), and its title becomes the title plus the frame value (text.frameOf, formatted by the field's format). Value axes span every frame, so they hold still while it plays. The element adds a Play button
series field bar line area scatter heatmap dumbbell ridgeline beeswarm parallel marimekko radial boxplot weave Split into series; heatmap row category; dumbbell exactly two (from, to); boxplot boxes side by side; weave one thread each (required)
path field[] treemap sunburst sankey; bar/line/area/dumbbell with drill Hierarchy outer to inner. On bar/line/area/dumbbell replaces x
size field scatter constellation Bubble or star area (sqrt scale)
name field scatter beeswarm boxplot units Point identity and tooltip title; duplicates become #2, #3...; units one dot per row
totals string[] waterfall x values drawn as running-total bars
stack boolean | "percent" bar area Stack series instead of grouping. "percent" shows each category's visible values as shares of its total: the axis runs 0 to 100%, hidden series renormalise, and y is formatted as percent unless format sets it. Labels, tooltips and the table show the share, not the raw value. Negative values are shares of the summed magnitudes and stack below 0
horizontal boolean bar dumbbell Categories on the left axis

Formatting fields

Field Type Default Meaning
format preset / template / {[field]: preset / template / options} auto Bare string applies to every y. Presets: auto integer decimal compact percent currency date month year time datetime. Template: "{value:percent} gross". Or Intl options plus optional prefix/suffix. Display only
titles {[field]: string} field names Display names everywhere (axis, tooltip, legend, table)
labels boolean false (heatmap: true at ≥24 px; treemap, sunburst: names; marimekko: shares; true) Formatted value on marks
text {[key]: string} English Localised UI strings with {0} placeholders: noData other back reset showing drilledInto zoomedTo selected selectionCleared chartOf chartOfAll firstOf measures crumbs positive negative above below vs ofTarget total shareOf sortedBy ascending descending fromTo reduced points point range density perCell mean rule play pause frameOf frame was since forms waffle bars swarm perDot count rank speedBy gross alike nearest max q3 median q1 min rows ofPrevious ofFirst
locale BCP 47 en-US Formatting locale
currency ISO 4217 USD Currency for the currency preset
title string - Visible heading and accessible name
description string auto Accessible description
yDomain [min, max] - Fixed value-axis domain. Pass [hi, lo] to reverse it (ranks with 1 on top). Not allowed with y array
xDomain [min, max] - Fixed x domain (scatter only)
rules (number | "mean" | {y, label?})[] - Reference lines across the value axis, at most 4, labels at most 40 characters. "mean" is the average of the visible values (stacked: of the category totals; stack "percent": of the segment shares, and numbers are shares such as 0.5) and reads "Average" without a label. Numbers widen the value axis; outside a yDomain they are not drawn. bar line area scatter

Interaction fields

Field Type Meaning
tooltip boolean Hover/keyboard tooltip (default true)
legend boolean Legend; clicking toggles series (default true when series is set, and for waffle, hexmap and units, where it toggles groups). Line and area charts drop the legend when direct end labels show, unless legend is set to true explicitly.
endLabels boolean Line and area: name each series at its right end with its last value (default true; 2 to 8 visible series, no value labels, no y2, width >= 400 px). false brings back the legend.
drill boolean Click/Enter zooms into a branch of path; breadcrumb, Back and Escape pop. Disabled at last level
drillOut boolean With drill, a click on empty chart space goes back up one level (default true)
select true / "multi" Click/Enter/legend selects marks; Escape clears. Not with drill
zoom boolean Drag to zoom (line, area, scatter); Reset, double-click, Escape restore
animate boolean Animate the first draw and every update (default true)

Time axes

Dates in x values are automatically detected and placed on a proportional time axis when using line, area or vertical bar charts. ISO 8601 dates are recognized as year-month ("2024-03"), full date ("2024-03-05") or date-time ("2024-03-05T14:30:00Z"); a date-time without an offset is read as UTC. A bare year such as "2024" stays a category. Numbers are never auto-detected as dates; use xType: "time" explicitly to interpret them as epoch milliseconds. A time axis preserves the chronological order and spacing of dates, so sort and limit options keep a category axis instead. To disable time axis detection and use a category axis with ISO dates, set xType: "category". All ticks and boundaries are placed at UTC calendar boundaries.

Large data

A chart handles up to a million rows wherever the chart makes sense, by reducing what it draws. Line and area charts reduce long series to about one point per 2 px of plot width (at most 1000 categories, and 4000 shared between series) using the LTTB (Largest Triangle Three Buckets) downsampling algorithm, keeping each series' first, last, minimum and maximum points so trends and extremes remain visible. A time axis reduces whenever it has more points than that budget; a category axis reduces past 1000 categories, over the category index. A kpi thins its sparkline the same way. The data table and screen reader description indicate how many points are displayed. A bar with more categories than fit (past 10000 marks) and no limit keeps the top N by total, N being 10 per 40 px of plot width, and rolls the rest into "Other". Scatter and beeswarm charts above 10000 visible points are drawn as density cells. A table draws the rows that fit its height, and its hidden data table lists the first 1000. The bar roll-up also adds a sentence to renderParts(...).warnings. Charts that cannot reduce without changing meaning (waterfall, dumbbell, parallel and the module charts) fail with too-many-marks, naming the mark count, the 10000 cap and the row count, and suggest limit or aggregate.

The row pass (grouping, aggregation, time parsing, validation) is cached per data array: a resize, legend toggle, zoom or view change re-renders without walking the rows again. The cache is keyed by the array's identity, length, first row and last row, so a push, a shift or a replaced last row is noticed; after editing any other row in place, assign a new array (chart.data = [...rows]).

Style fields

Field Type Meaning
colors string[] or {[series]: color} Max 8; slot assignment stable across updates. 9+ series wrap slots
colorBy "sign" / {target: n} / field Tone by sign of y, by a target value, or a ramp by a numeric field. Not with series
theme {[token]: css} Theme token overrides (CSS values, allowlisted)
grid boolean Grid lines perpendicular to the value axis (default true)
xAxis boolean Bottom axis (default true)
yAxis boolean Left axis (default true)
table boolean Visually hidden data table for screen readers (default true)

Examples

1. Currency bar

{ "type": "bar", "x": "month", "y": "revenue", "format": "currency",
  "data": [{ "month": "Jan", "revenue": 10500 }] }

Vertical bar chart with currency-formatted values.

2. Stacked bar

{ "type": "bar", "x": "state", "y": "units", "series": "region", "stack": true,
  "data": [{ "state": "CA", "region": "West", "units": 120 }] }

Horizontal regions, stacked per state.

3. Horizontal sorted limited labelled bar

{ "type": "bar", "horizontal": true, "x": "product", "y": "margin",
  "sort": "desc", "limit": 5, "labels": true,
  "data": [{ "product": "A", "margin": 22 }] }

Top 5 products by margin, descending, with on-bar labels.

4. Sign-coloured percent bar

{ "type": "bar", "x": "metric", "y": "variance", "format": "percent",
  "colorBy": "sign",
  "data": [{ "metric": "revenue", "variance": 0.15 }] }

Bars toned green (positive) or red (negative).

5. Multi-measure line with date format and zoom

{ "type": "line", "x": "date", "y": ["revenue", "units"], "zoom": true,
  "format": { "date": "date", "revenue": "currency" },
  "data": [{ "date": "2024-01-01", "revenue": 10000, "units": 50 }] }

Two measures on the y-axis with a measure toggle; drag to zoom, Reset to restore.

6. Stacked area

{ "type": "area", "x": "month", "y": "sales", "series": "region", "stack": true,
  "data": [{ "month": "Jan", "region": "North", "sales": 1500 }] }

Regions stacked over time.

7. Waterfall with totals

{ "type": "waterfall", "x": "stage", "y": "amount", "totals": ["Q1", "FY"],
  "data": [{ "stage": "Q1", "amount": 100 }] }

Flow chart with Q1 and FY marked as running totals.

8. Bubble scatter with select

{ "type": "scatter", "x": "population", "y": "gdp", "size": "area", "name": "country",
  "select": "multi",
  "data": [{ "country": "USA", "population": 331, "gdp": 23, "area": 9.8 }] }

Countries as bubbles; click to select multiple.

9. Heatmap count

{ "type": "heatmap", "x": "hour", "y": "traffic", "aggregate": "count",
  "data": [{ "hour": "09", "traffic": "high" }] }

Count of observations per hour and traffic level.

10. Treemap drill

{ "type": "treemap", "path": ["region", "state", "product"], "y": "revenue",
  "drill": true,
  "data": [{ "region": "West", "state": "CA", "product": "A", "revenue": 5000 }] }

Hierarchical treemap; click to drill into regions, states, and products.

11. Weave

{ "type": "weave", "x": "year", "y": "sales", "series": "team",
  "data": [{ "year": "2024", "team": "A", "sales": 10 }, { "year": "2024", "team": "B", "sales": 12 },
    { "year": "2025", "team": "A", "sales": 15 }, { "year": "2025", "team": "B", "sales": 9 }] }

Ranks per year as threads that cross over and under. Import mayacharts/weave.

12. Units

{ "type": "units", "x": "plan", "y": "spend", "name": "customer",
  "data": [{ "customer": "c1", "plan": "Pro", "spend": 120 }, { "customer": "c2", "plan": "Free", "spend": 0 }] }

One dot per customer that flies between waffle, bars and swarm. The legend toggles plans. Import mayacharts/units.

13. Orbit

{ "type": "orbit", "x": "product", "y": "revenue", "y2": "growth",
  "format": { "growth": "percent" },
  "data": [{ "product": "A", "revenue": 500, "growth": 0.12 }, { "product": "B", "revenue": 300, "growth": -0.05 }] }

Planets sized by revenue; growth sets each planet's speed and direction. Import mayacharts/orbit.

14. Constellation

{ "type": "constellation", "x": "account", "y": ["revenue", "seats", "tickets"],
  "data": [{ "account": "Acme", "revenue": 120, "seats": 40, "tickets": 3 },
    { "account": "Globex", "revenue": 80, "seats": 25, "tickets": 9 },
    { "account": "Initech", "revenue": 95, "seats": 30, "tickets": 4 }] }

Accounts placed by how alike their measures are. Import mayacharts/constellation.