Skip to content

Axes & number formatting

Set axis bounds, titles, scales and numeric tick formatting. This feature belongs to @revolist/charts and can be used without Pivot or a grid.

Choose Change settings to expand the Y range; restore it to compare the same data at the original scale. The source observations stay unchanged.

Numeric axes support linear, logarithmic, symlog and signed square-root scales. Named axes, numeric X, grouped category labels and independent bar value axes are described below; support depends on the chart family.

Loading chart…

Inspect live chart state

The chart uses the native Charts renderer. Buttons outside the plot are example application controls calling the public API.

Complete chart setup with sample data

Install Charts using the installation guide, then run this setup after the host mounts. The data is illustrative. Additional actions for this tool are explained below.

<div id="chart" style="width:100%;height:500px"></div>
import { createChartFactory } from '@revolist/charts/core';
import { lineChart } from '@revolist/charts/line';
import { groupedColumnChart } from '@revolist/charts/grouped-column';
import { columnLineChart } from '@revolist/charts/column-line';
const rows = [
{
"date": 1767225600000,
"month": "Jan",
"revenue": 65,
"costs": 45
},
{
"date": 1769904000000,
"month": "Feb",
"revenue": 82,
"costs": 50
},
{
"date": 1772323200000,
"month": "Mar",
"revenue": 76,
"costs": 48
},
{
"date": 1775001600000,
"month": "Apr",
"revenue": 95,
"costs": 65
},
{
"date": 1777593600000,
"month": "May",
"revenue": 110,
"costs": 72
},
{
"date": 1780272000000,
"month": "Jun",
"revenue": 102,
"costs": 68
},
{
"date": 1782864000000,
"month": "Jul",
"revenue": 121,
"costs": 80
},
{
"date": 1785542400000,
"month": "Aug",
"revenue": 115,
"costs": 76
},
{
"date": 1788220800000,
"month": "Sep",
"revenue": 135,
"costs": 89
},
{
"date": 1790812800000,
"month": "Oct",
"revenue": 128,
"costs": 85
},
{
"date": 1793491200000,
"month": "Nov",
"revenue": 145,
"costs": 92
},
{
"date": 1796083200000,
"month": "Dec",
"revenue": 155,
"costs": 100
}
];
const mapping = {
"category": {
"field": "date",
"label": "month"
},
"series": [
{
"field": "revenue",
"label": "Revenue"
},
{
"field": "costs",
"label": "Costs"
}
]
};
const createChart = createChartFactory([lineChart, groupedColumnChart, columnLineChart]);
const container = document.getElementById('chart')!;
const chart = await createChart({
container,
data: rows,
mapping,
"chartType": "columnLine",
"axes": {
"x": {
"scale": "time"
},
"y": {
"min": 0,
"max": 200,
"title": "Revenue (€k)",
"tickFormat": {
"maximumFractionDigits": 1
}
}
},
"presentation": {
"title": "Monthly performance",
"subtitle": "Illustrative data · 2026",
"legendPosition": "bottom"
},
"interaction": {
"mode": "select",
"selectionMode": "multiple"
}
});
// When the host unmounts:
// chart.destroy();

Standalone Charts and Pivot use the same axis configuration. Existing x, y and ySecondary aliases remain supported.

axes: {
x: {
scale: 'log',
position: 'top',
reverse: true,
tickValues: [1, 10, 100],
title: 'Elapsed time',
unit: 'ms',
},
y: {
min: 0,
max: 100,
position: 'right',
tickCount: 5,
tickSpacing: 48,
title: 'Utilization',
unit: '%',
},
}

Physical horizontal axes support top, bottom and none; vertical axes support left, right and none. This includes additional logical Y value axes on horizontal bars. none hides axis lines, ticks, category labels and titles while retaining the plot and grid. Cartesian axis positions apply to category/time axes too. Matrix and polar positions are not supported yet. Two combination Y axes on the same side receive separate gutters.

Numeric X axes and supported value axes accept reverse. This mirrors both projection and inverse projection: navigation, brushes and annotations use the same raw values. Saved viewport and range min/max remain increasing. Category/time reversal is supported by ordered chart families advertising categoryAxisReverse in their catalog definition: navigable line/area, column/bar, combination, financial, statistical and small-multiples charts. On horizontal bars the category axis is still configured as axes.x and projects along screen Y. Other category families reject reversal until their geometry supports it.

Ordinary line and area families support primary Y bounds, units, formatting and numeric scales. Log axes require positive observations. Areas use the positive domain edge as their log baseline. Line/area paths are clipped to authored domains; out-of-domain marks are omitted. Bounds on normalized areas use percentage units.

Numeric tick controls:

  • tickValues: an explicit list of up to 1000 unique finite numbers. Values outside the domain are omitted; an empty list hides numeric tick labels. This overrides automatic density and spacing controls.
  • tickCount: desired maximum count from 2 to 1000, while retaining readable intervals. An explicit maximum step can request more ticks.
  • tickMinStep / tickMaxStep: positive lower/upper limits for automatic intervals, in raw data units. Minimum step, pixel spacing and the 1000-tick safety bound take precedence when limits conflict. Log scales otherwise use decade ticks.
  • tickSpacing: minimum pixel spacing between automatic ticks. A shorter axis shows one midpoint tick.

These controls change ticks without changing the scale domain. Compact navigator previews retain their own reduced tick density. Pivot exposes numeric position, reversal and tick controls in its axis inspector. Axis options are included in model save/restore and server-rendered SVG.

Line/area and combination charts also accept named X axes; all six grouped/stacked/normalized bar/column types accept named categorical axes. Other families retain their catalog-specific scale capabilities.

Numeric X on line/area and combination charts, and supported numeric value axes (line/area, combinations, grouped/stacked/normalized bars and columns, scatter/bubble/quadrant), also accept symlog and sqrt:

axes: {
x: { scale: 'symlog', constant: 10 },
y: { scale: 'sqrt', min: -100, max: 100 },
}

Symlog maps sign(value) * log1p(abs(value) / constant). Its positive finite constant defaults to 1 and controls the transition near zero. A constant is valid only on a symlog axis. Square root uses the signed transformation sign(value) * sqrt(abs(value)), allowing negative values and zero. These match the numeric scale families described by MUI and the signed D3 power-scale convention.

Zoom windows, brushes, annotations, CSV and saved models retain raw data values. Symlog windows inherit the constant from their axis; the viewport does not repeat it. Reverse, numeric tick policies, full-source domains and SSR use the same projection/inverse pair. Automatic ticks follow transformed spacing on wide domains and readable raw intervals on narrow domains. Charts sync groups require matching symlog constants as well as scale types and units.

Pivot exposes both scale types and a positive symlog constant field; switching away removes the incompatible constant. Other Cartesian families still follow their declared axis capabilities.

Ordinary line/area families, combination charts, all six grouped/stacked/normalized bar/column types and scatter/bubble/quadrant charts accept additional value axes in axes.yAxes. Define stable IDs, then assign series with seriesOverrides[seriesId].yAxis:

axes: {
y: { title: 'Revenue', unit: '$' },
ySecondary: { title: 'Units', position: 'right' },
yAxes: {
temperature: {
title: 'Temperature',
unit: '°C',
min: -40,
max: 60,
position: 'left',
},
utilization: {
title: 'Utilization',
unit: '%',
min: 0,
max: 100,
position: 'right',
},
},
},
seriesOverrides: {
units: { yAxis: 'secondary' },
temperature: { yAxis: 'temperature' },
utilization: { yAxis: 'utilization' },
},

Omitted assignments use primary (axes.y). secondary remains the alias for axes.ySecondary. Named vertical value axes default to the right; horizontal bar value axes default to the top. Axes on the same side receive separate gutters, in record order. Their scale, bounds, title, colors, tick policy and reversal are independent. Declared named axes remain present when a series is hidden. Use position: 'none' to hide decorations without removing the assignment.

Names must be nonblank strings up to 128 characters. A collection supports up to 32 named axes. The aliases primary, secondary, x, y, ySecondary, and prototype-related names are reserved. Unknown assignments are rejected, including assignments on hidden series; removing an axis requires clearing or reassigning its series in the same update.

Line/area stacks and normalized percentages are calculated independently for each assigned axis. Combination column stack groups are also scoped by axis ID. Canonical cell/series IDs, colors, click events and CSV columns are preserved. Shared tooltip units, error bars and analytical overlays use the assigned axis. Model save/restore and SSR retain the collection. The global Y synchronization and full-source Y metadata continue to describe the primary alias; author explicit bounds when other axes need a fixed domain across remote detail windows.

Value references can target a named axis with axisId; their legacy axis discriminator remains primary or secondary:

annotations: {
references: [
{
id: 'freezing', kind: 'line',
axis: 'primary', axisId: 'temperature',
value: 0, label: 'Freezing',
},
],
shapes: [
{
id: 'temperature-note', kind: 'note', yAxis: 'temperature',
text: 'Target',
at: { x: { scale: 'category', path: [{ field: 'month', value: 'Jan' }] }, y: 20 },
},
],
}

References/shapes retain their chart type’s annotation capabilities. Pivot’s inspector exposes configured named axes and per-series assignment for these families; definitions can be supplied through the model/API. Chart-type changes remove unsupported definitions and assignments together.

Specialized annotated time series, matrix and other families retain their existing value-axis contracts. Primary financial price options are described in Candlestick and OHLC price axes. Relationship binding and navigation rules are covered in Multiple value axes on relationship charts. Horizontal bar value axes use the orientation rules in Multiple value axes on bars and columns. Multiple X axes also apply to grouped/stacked/normalized bars and columns with categorical scales.

Ordinary line/area families, combination charts and all six grouped/stacked/normalized bar/column types accept axes.xAxes and per-series xAxis assignments. The existing axes.x is named primary. Bars/columns support categorical X assignments; line/area/combinations also support their declared time/numeric scales. Named horizontal category axes default to the top; named categories on horizontal bars are vertical and default to the right. Axes sharing a side receive separate measured lanes.

axes: {
x: { scale: 'log', level: 0, title: 'Frequency' },
xAxes: {
elapsed: { scale: 'linear', level: 1, reverse: true, title: 'Elapsed' },
},
},
seriesOverrides: {
response: { xAxis: 'elapsed' },
},
navigation: { enabled: true, xAxis: 'elapsed', navigator: true },
viewport: {
x: { scale: 'log', min: 1, max: 100 },
xAxes: { elapsed: { scale: 'linear', min: 10, max: 50 } },
},

Each X definition selects a zero-based category path level. Omit level to use the last path member. Alternatively, data maps stable category IDs to raw scalar X values; missing mapping keys fall back to the selected path member. Numeric axes require finite numbers (positive for logarithmic axes), and time axes require valid UTC instants. Category/series identities and source row order remain unchanged.

X definitions independently control scale, bounds, tick policy, direction, title and placement. Series sharing a Y axis share its domain across X assignments. Area normalization and combination column stacks are scoped to their X/Y pair; a reused stack-group name does not combine values across X axes. Range-area lower helpers use their owner layer’s X projection. Layer ordering and explicit zIndex apply across X-axis groups.

navigation.xAxis selects the axis for gestures, toolbar actions, navigator and calendar controls. Reset clears only that X window. rangeSelection.xAxis independently selects a brush axis. Hover reports the assigned raw numeric/time X value; shared tooltip rows stay within that axis group. Visible calculated references and semantic datum notes use their series’ assigned window. Current-scope CSV includes the union of visible canonical rows, leaving a series cell blank outside its own window; all-scope export retains the full canonical table.

Up to 32 named definitions are accepted. Unknown assignments and reserved IDs (primary, x, and prototype-related names) are rejected. The same API is accepted by the standalone Pivot adapter. Analytical overlays inherit their series’ X/Y assignment. Sync groups opt into named identities using xAxes: ['elapsed'], preserving unselected windows; see synchronization. Pivot exposes named-X definitions, series assignments and navigation/brush targets in Customize; see Pivot authoring.

Shapes and category references select an X projection using xAxis. Omit it to use primary. The reference discriminator axis: 'category' means the chart’s logical X axis, including numeric and time scales; value-axis references continue to use axis/axisId for Y.

annotations: {
editable: true,
shapes: [{
id: 'elapsed-note', kind: 'note', xAxis: 'elapsed',
at: { x: { scale: 'linear', value: 25 }, y: 20 },
text: 'Elapsed target',
}],
references: [{
id: 'elapsed-band', kind: 'band', axis: 'category', xAxis: 'elapsed',
from: { scale: 'linear', value: 10 },
to: { scale: 'linear', value: 30 },
}],
}

Raw coordinates must match the assigned X scale. Logarithmic values must be positive, time values must be valid UTC instants, and band endpoints must increase in raw units even when reversed. Category references and shapes retain semantic paths on category axes. A shape can select a Y axis separately through its existing yAxis property; series-anchored notes infer both axes from their target series.

The drawing editor offers an X-axis selector when multiple projections exist. Reassignment converts through the old projection and the new inverse projection, preserving the draft’s visible placement. Pointer and keyboard edits retain the assignment; Save commits it and Cancel leaves the authored model unchanged. New drawings start on the configured navigation X axis. Unknown assignments and incompatible coordinate-scale updates are rejected before replacing the current frame. Model save/restore and SVG server rendering preserve the assignment. Controls and editor behavior are shared by standalone Charts and Pivot.

Use axes.x: { scale: 'category', reverse: true } or { scale: 'time', reverse: true }. Canonical dataset order, category IDs, chronological path order and exported values stay unchanged. Projection, nearest-point lookup, labels, references, navigation and range brushes mirror together. Saved category endpoint IDs remain in canonical increasing order; saved time bounds remain increasing epoch milliseconds. Navigator handle start/end mean physical left/right edges (top/bottom for vertical category brushes). For a reversed time axis, the start handle therefore carries the later timestamp. ARIA numeric limits remain increasing, and navigator formatter callbacks receive the same raw value as aria-valuenow. Category handle values are visual boundary ordinals, with the end boundary exclusive.

Ordered category charts expose categoryAxisGroups in the chart catalog. Use axes.x.groups to replace flattened category labels with inner-to-outer bands. The same logical category axis projects vertically for horizontal bars; its physical placement/color/title still use axes.y.

axes: {
x: {
scale: 'category',
groups: [
{ level: 2, size: 32, tickSize: 6 }, // month, nearest the plot
{ level: 1, labelColor: '#334155' }, // quarter
{ level: 0, lineColor: '#64748b' }, // year, outermost
],
},
}

level is a zero-based category path index (0..127); omitted uses the final path member. Labels come from the matching category.labels entry, with the raw value as fallback. A missing path level has no text and stays available if later data supplies it. values can map stable category IDs to finite scalar group values (string, number, boolean or null); unmatched IDs fall back to the path rule. Equal labels with distinct typed values stay distinct. Only consecutive visible categories coalesce; parent-band identities also split child runs, preventing the same quarter in two years from merging.

Up to eight bands are supported. size is band thickness (16..160 SVG pixels; defaults to 32 horizontally or 80 vertically), and tickSize is 0..24 (default 6). Colors use the existing hexadecimal contract. Band sizes shrink proportionally to fit short/narrow hosts and facet panels. Text that cannot fit is omitted by the shared label layout; semantic data and marks remain intact. Titles and navigation reserve the grouped gutter. Hidden axes hide the bands, and compact navigator previews omit them. Named-Y render groups draw each category axis once; small multiples get independent bands per panel.

For computed grouping, use runtime callbacks:

rendererOptions: {
categoryAxis: {
groups: [
{ getValue: (category) => category.path.at(-1) ?? null },
{ getValue: (category) => ({ value: category.path[0], label: `Year ${category.path[0]}` }) },
],
},
}

Runtime rows accept the same size/tick/color options and override the model’s band list. Callback values must be finite scalars or { value, label } pairs. Each row/category is evaluated once per axis per render, even when multiple plots share that axis. Callbacks stay outside saved models; supply them again when restoring. Serializable path/value groups are retained by save/restore and SSR. Grouping applies only to category scales; explicit groups on time/numeric or unsupported chart families are rejected. Runtime group callbacks are inactive on those families/scales. Time/numeric axis grouping remains separate from this categorical feature.

Reversal and viewport windows use the ordinary projection and canonical IDs. A partial group labels only its visible span. Group labels are decorative and never become fabricated data-click targets. Pivot exposes a grouping checkbox that starts from the current hierarchy, with editable path levels and add/remove controls. Scale/type transitions clear incompatible groups atomically.

This covers grouped band/point label rows described by MUI grouped axes. Supported Cartesian families use the measured gutters described below. Named X-axis assignment is supported on the line/area, combination and categorical bar/column families described above.

For supported line/area, combination and bar/column charts, Customize → Axes exposes additional X axes with stable IDs. Add an axis, choose its scale and category path level, then assign series to it. Existing category-ID value mappings are preserved and identified in the form; “Use category path values” switches back to path projection. Scale, grouping, placement, reversal, title/unit and numeric bounds/ticks reuse the shared axis forms. Blank path levels use the automatic final path member.

Customize → Zoom and navigation selects the navigation X axis and brush X axis independently. The date-range option follows the selected navigation axis; resetting the view clears all authored windows. Removing an axis reassigns its series to primary, removes its window and incompatible shapes/references, clears its brush range, and returns its navigation target to primary. Calendar analysis that no longer targets a time axis is removed. Unrelated windows, annotations, overlays and series styles are retained. Scale/type changes and Reset appearance apply the same cleanup atomically. Direct API updates remain strict and reject incompatible definitions; this cleanup is an inspector policy. Invalid bounds/ticks stay out of the host model and can be corrected in the form.

Grouped/stacked/normalized bar and column, line, smooth/stepped line, ordinary/stepped/stacked/normalized area, column-line, area-line, scatter, bubble and quadrant charts measure their axis text before painting marks. Browser SVG metrics include the current font, label styles, formatter text and classes. Each visible axis gets its own gutter; axes placed on the same side use cumulative measured offsets. Numeric X labels clear their baseline at large font sizes. Hidden axes reclaim space. Titles, legend and navigation keep separate reservations, and gutters in crowded/narrow hosts share at most 65% of the available frame in each dimension; the existing label-fitting policy handles text that cannot fit.

Built-in renderers explicitly opt into measurement; custom native renderers retain their single-pass behavior. The measurement pass paints axes only and is removed before interaction, exports and the final render callback. Axis/category label formatter results are cached by semantic identity within that render, so measurement and painting reuse the same result. Callback context includes axisId (primary, secondary, or a declared named ID), alongside the physical axis; named vertical value axes use axis: 'y' with their own axisId; horizontal bar value axes use axis: 'x'. The existing vertical secondary alias remains axis: 'ySecondary'. Plain result/style records are copied so a formatter can reuse a mutable result object safely. Resizing or a subsequent update measures the current frame again. Fonts should be loaded before creation, or trigger an update after changing font resources.

SSR uses deterministic font-size/text-length fallback measurements when SVG glyph metrics are unavailable. Browser and server gutters can differ with platform fonts; authored data, axes and domains remain unchanged. Preview thumbnails retain compact layout. Distribution, financial, range, matrix and facet renderers still use their existing layout policy; measured sizing across those families remains separate work.

The six grouped/stacked/normalized bar and column types configure their logical primary value scale through axes.y. It accepts linear, positive logarithmic, signed symlog and signed square-root scales, min/max, reversal, tick values/count/spacing, formatting and units. Stacks retain independent positive/negative accumulations per stack group. Normalized charts use their normalized target units for bounds, ticks and clipping; raw dataset/callback/CSV values stay unchanged.

Horizontal bars preserve their existing physical appearance contract: axes.x controls the visible X line/colors/title/position, while axes.y controls the visible Y line/colors/title/position. axes.x.reverse still reverses categories; axes.y.reverse independently reverses numeric values. Numeric bounds/scale/ticks and the primary unit come from axes.y in both orientations. A legacy horizontal axes.x.unit remains a fallback when no Y unit is authored.

// groupedBar: horizontal values, vertical categories
axes: {
x: { reverse: true, position: 'top', title: 'Revenue' },
y: { scale: 'symlog', constant: 2, min: -100, max: 100,
reverse: true, unit: 'USD', position: 'right', title: 'Region' },
}

Rectangular segments clip to the authored numeric domain. Partially visible bars retain their raw identities and tooltip values; fully disjoint segments produce no mark. Clipped endpoints do not produce point annotations. Logarithmic bars require positive observations and a positive domain; their zero baseline clips to the positive lower bound. Without an authored lower bound, the renderer chooses a positive floor at or below one, below the smallest positive endpoint where finite representation permits.

Grouped and stacked types expose primary/category references, point notes and drawing shapes. Stacked notes follow the actual accumulated endpoint, and raw references use the same multiplier-safe projection as marks. Normalized types retain their previous annotation capability policy. Error overlays remain grouped-only. Value limits stay separate from category viewport navigation. All six types use measured gutters with orientation-aware grouped category bands. In Pivot, horizontal bars expose Primary value axis and Category axis forms that route settings through this shared contract; category order retains its existing reversal/group controls.

All six grouped/stacked/normalized bar and column types accept axes.ySecondary, named axes.yAxes, and seriesOverrides[id].yAxis. Each series uses its selected numeric scale, bounds, unit, reversal and ticks. Stacks and normalization stay independent for each value-axis ID and stack group. A large overflowing primary stack cannot rescale a tiny secondary stack. Public data, callbacks and exports retain their original values.

axes: {
y: { min: 0, max: 100, unit: 'kg' },
ySecondary: { min: 0, max: 5000, unit: 'USD', position: 'top' },
yAxes: { temperature: { scale: 'symlog', unit: 'C', position: 'top' } },
},
seriesOverrides: { revenue: { yAxis: 'secondary' }, heat: { yAxis: 'temperature' } },

This example uses horizontal bars. Additional horizontal value axes accept top, bottom or none, defaulting to top; columns accept left, right or none, defaulting to right. The primary horizontal appearance aliases described above remain unchanged. All value axes share measured orientation-aware gutters and one canonical category projection. Grouped/stacked references, shapes and notes project through the selected value axis; grouped error bounds contribute to their own assigned scale. Normalized annotation and grouped-only error policies remain unchanged.

Pivot exposes secondary and declared named value-axis forms and series assignments. Gallery changes between horizontal bars and vertical charts clear incompatible additional-axis positions while preserving scales, numeric settings, units and valid assignments. Direct API updates validate positions strictly and reject an incompatible transition atomically. Full-source and synchronized Y domain metadata continues to describe the primary value axis; additional value axes use authored or local data bounds. Named categorical assignments follow the separate category-axis contract below.

Runtime full-domain metadata is a renderer contract. The async ChartDataSource acquisition API currently requires numeric/time X bounds; these categorical bar/column types do not acquire remote detail windows through that API.

Multiple category axes on bars and columns

Section titled “Multiple category axes on bars and columns”
axes: {
x: { level: 0 },
xAxes: { labels: { scale: 'category', level: 1, reverse: true, position: 'right' } },
y: { unit: 'kg' },
},
seriesOverrides: { actual: { xAxis: 'primary' }, budget: { xAxis: 'labels' } },
viewport: { xAxes: { labels: { scale: 'category', startId: dataset.categories[1].id, endId: dataset.categories[3].id } } },
navigation: { enabled: true, navigator: true, xAxis: 'labels' },
rangeSelection: { enabled: true, xAxis: 'labels' },

This example uses a horizontal bar chart. Named categorical axes support left, right or none on bars, defaulting to right. Columns use top, bottom or none, defaulting to top. The legacy primary horizontal appearance aliases remain unchanged: physical X settings come from axes.x, and physical Y appearance comes from axes.y. Primary logical category level, data, groups and reverse still come from axes.x. Additional category appearance belongs to its axes.xAxes[id] definition.

Each category axis uses the canonical category IDs with independent inclusive windows, reversal, labels and grouped bands. level selects a path member; category-ID data mappings override its labels. Category definitions reject time/numeric scales. Series keep canonical adjacent slots and stable callback identities; stacking and normalization are partitioned by X ID, Y ID and stack group. Value-axis domains include all their assigned X groups without summing unrelated stacks.

navigation.xAxis chooses gestures, toolbar, navigator and native auto-scroll; an authored target window disables auto-scroll. Reset preserves other windows. rangeSelection.xAxis independently targets the semantic brush. Notes, references, shapes, hover, compatible sync groups, CSV windows, saved models and SSR use the assigned projection; global value references paint once. Extra category label callbacks report their stable axisId and physical axis (y on horizontal bars, x on columns).

Pivot reuses the named-X definition, series assignment and target controls. Its horizontal-bar forms offer vertical category positions. Gallery transitions preserve compatible category mappings, groups and assignments while clearing incompatible positions or continuous-axis definitions. Direct updates validate strictly and reject before replacing the frame.

Time axes choose real UTC calendar tick boundaries and responsive year, quarter, month, date or intraday labels. Daily/monthly views omit timestamp noise. Custom localeText.renderer.formatTimeAxisTick formatters receive the actual tick epoch, the date-only flag and the adaptive default text.

await chart.update({ axes: {
x: { scale: 'time', interval: 'month', missingPeriods: 'gap' },
} });

interval sets the minimum tick cadence. With missingPeriods: 'gap', it also defines which missing periods break a line or area path. For example, February followed by April has a gap for March. Without the policy, existing finite observations remain connected. Explicit nulls break paths under either policy; zero stays a real observation. No missing source rows are invented.

See Calendar comparisons for previous-period overlays; a date-range preset changes the viewport rather than calculating a comparison.

Multiple value axes on relationship charts

Section titled “Multiple value axes on relationship charts”

Scatter, bubble and quadrant charts assign each bound Y measure through seriesOverrides[id].yAxis. A single X or size binding still broadcasts across groups; parallel bindings retain their canonical group identity.

bindings: { x: ['cost'], y: ['revenue', 'margin'], size: ['employees'] },
axes: {
x: { scale: 'linear', title: 'Cost' },
y: { min: 0, max: 100000, unit: 'USD' },
yAxes: {
margin: { scale: 'symlog', constant: 1, min: -100, max: 100,
unit: '%', position: 'right', reverse: true, title: 'Margin' },
},
},
seriesOverrides: { margin: { yAxis: 'margin' } },

Map all referenced fields as numeric series. size applies to bubble charts; omit it for scatter and quadrant charts. primary selects axes.y, secondary selects axes.ySecondary, and other IDs select axes.yAxes[id]. Only fields in bindings.y accept non-primary X/Y assignments; assigning an X-only, size-only or unused helper field is rejected. Explicit primary helper assignments remain accepted.

Each Y axis independently supports linear, log, symlog and signed sqrt, constant for symlog, authored min/max, reverse, left/right/none placement, title, units, colors, notation, precision and numeric tick controls. Automatic domains use only that axis’s bound observations and attached Y error bounds. Logarithmic validation follows the assigned bound measures. Declared axes paint once with measured same-side gutters, even when no group uses them.

Marks, canonical click payloads, shared tooltip rows and crosshairs, errors, regression overlays, semantic notes, numeric-X shapes and value references use the bound Y projection. References select named values with axisId; shapes use yAxis. Relationship charts support value references and notes; numeric X references use the legacy category discriminator as described below. Quadrant backgrounds and pivotY use the primary Y scale; each mark’s color follows the region where it is displayed, including reversed axes.

viewport.y, Y gestures, full-source Y bounds and synchronized Y domains still control the primary axis. Secondary/named Y axes retain their authored or local domains. Hover on their marks synchronizes X only, so another chart never interprets their values in primary Y units. Groups may independently select numeric X axes as described below. Current-scope CSV retains a canonical row visible in any bound group and preserves its columns. Save/restore and server-rendered SVG retain Y definitions and assignments.

Pivot exposes primary, secondary and declared named Y forms. Its per-series Y selector includes only bound Y measures. Gallery transitions retain compatible bound Y assignments and clear non-primary assignments on fields becoming X/size helpers. API updates reject unknown IDs, incompatible definitions and invalid log data before replacing the current chart.

Independent numeric X axes on relationship charts

Section titled “Independent numeric X axes on relationship charts”

Relationship groups now accept axes.xAxes[id] and seriesOverrides[boundYId].xAxis. Both X and Y axis assignments belong to the group’s bound Y measure. The corresponding bindings.x measure supplies raw X observations; a single binding still broadcasts. X-only and size-only helpers cannot select non-primary axes.

bindings: { x: ['cost', 'elapsed'], y: ['revenue', 'temperature'] },
axes: {
x: { scale: 'linear', min: 0, max: 10000, unit: 'USD' },
xAxes: {
elapsed: { scale: 'log', min: 1, max: 1000, reverse: true,
position: 'top', unit: 'ms', title: 'Elapsed time' },
},
yAxes: { temperature: { min: -40, max: 60, unit: 'C' } },
},
seriesOverrides: { temperature: { xAxis: 'elapsed', yAxis: 'temperature' } },
viewport: { xAxes: { elapsed: { scale: 'log', min: 10, max: 100 } } },
navigation: { enabled: true, xAxis: 'elapsed', navigator: true },
rangeSelection: { enabled: true, xAxis: 'elapsed' },

Relationship X definitions default to linear; log, symlog (with positive constant) and signed sqrt are supported. Each X axis independently owns bounds, unit/notation/precision, reversal, numeric ticks, top/bottom/none placement, colors and title. Named axes default to the top and share measured gutters. Category/time scales, category level/data mappings and grouped category bands are rejected: use numeric bindings.x. Stable IDs, reserved-name rules and the 32-axis limit are the same as other named X collections.

Automatic domains include their assigned groups and attached X error bounds; authored bounds take precedence. Partial authored bounds resolve a finite opposite endpoint even when observations lie outside the authored edge. Navigator previews preserve independent Y scales while omitting additional axis decorations. Log validation checks their actual bound X observations and errors. viewport.x controls primary X; viewport.xAxes[id] controls that named X only. navigation.xAxis selects gestures, toolbar and navigator. Reset clears the selected X window and the common primary Y window while preserving other X windows. rangeSelection.xAxis independently targets a raw numeric brush. Declared unused X axes retain empty navigation geometry without inventing observations.

Numeric X references use axis: 'category', xAxis and raw scaled coordinates; this legacy discriminator names the logical X axis. Shapes select xAxis separately from yAxis. Semantic notes, error bars, crosshairs, regression overlays, saved models and server-rendered SVG use the assigned projection. Regression retains its bound xSeriesId requirement when several X bindings broadcast to one Y field. Shared tooltip rows stay within the activated X axis. Current-scope CSV includes the union of rows visible in any group and keeps their canonical columns; it does not blank individual cells. All-scope export retains the complete canonical table.

Sync groups opt into named identities through xAxes: ['elapsed'], preserving unselected windows. Hover carries xAxis with the raw X value. Compatible scale, units and symlog constants are required. Async source requests carry named windows; fullDomain.xAxes[id] fixes the named source domain across detail frames, and runtime overviews use the selected group’s bound X/Y fields. Primary full-domain and synchronization metadata remain independent.

Pivot adds numeric named X definitions and exposes X/Y assignment selectors only for bound Y measures. These forms omit category path controls. Gallery transitions strip category projections and helper assignments while retaining compatible numeric settings; direct API updates reject incompatible fields atomically.

Candlestick and OHLC charts support a primary numeric price axis through axes.y:

axes: {
x: { scale: 'time', reverse: true },
y: {
scale: 'log', min: 1, max: 1000, position: 'right',
title: 'Price', unit: 'USD', reverse: false,
tickValues: [1, 10, 100, 1000],
tickFormat: { notation: 'standard', maximumFractionDigits: 2 },
},
},
navigation: { enabled: true, crosshair: true },

Bind the four distinct open, high, low and close measures. The existing four-series limit remains. Each complete observation normalizes its extrema across all four values. Incomplete rows remain gaps and do not contribute a price domain. Default and explicit linear scales retain padded observed extrema, without forcing a zero baseline. Category/time X navigation changes the visible observations without recomputing the full price domain.

Price axes accept linear, log, symlog and signed sqrt; symlog accepts a positive constant (default 1). Complete OHLC observations must be positive on log axes. min/max, reverse, left/right/none placement, title, unit, lineColor, labelColor, titleColor, tickFormat.notation/minimumFractionDigits/maximumFractionDigits, tickValues, tickCount, tickMinStep, tickMaxStep and tickSpacing use the shared numeric contracts above. Partial bounds resolve a finite opposite endpoint even when all observations lie outside the authored edge. Runtime full-source/synchronized primary Y bounds use the same price projection.

Candles, OHLC/HLC/high-low ticks, and line/step-line price modes clip to the price plot. Fully outside candles and out-of-domain close markers are omitted; a visible wick can remain when its body is outside the domain. Rising/falling colors still compare the original close/open values under reversal. Clipping intersections are decorative geometry, and callback payloads, canonical data, CSV values and saved observations retain raw prices. Notes and references use the same registered scale; notes anchored to out-of-domain values are not painted.

These two renderers measure styled/formatted category, tick and title text using the shared gutter owner, including resize and deterministic server-rendered fallback. Pivot exposes the primary price form automatically. Axis definitions persist in saved models and server-rendered SVG. Candlestick/OHLC price and X collections are described below. Volume-price has fixed price and volume controls described in Volume-price pane axes. Financial X supports category/time and the numeric scales described below; navigation, brush and annotation policies share their raw projection.

volumePrice uses two fixed roles: axes.y controls OHLC prices in the upper pane, and axes.ySecondary controls volume in the lower pane. Both panes share each paired group’s category/time/numeric X projection, including irregular dates, reversal, viewport windows and grouped category labels.

bindings: {
open: ['open'], high: ['high'], low: ['low'], close: ['close'],
volume: ['volume'],
},
axes: {
x: { scale: 'time', reverse: true, title: 'Trading session' },
y: { scale: 'log', min: 1, max: 1000, title: 'Price', unit: 'USD' },
ySecondary: {
scale: 'symlog', constant: 5, min: -5000, max: 5000,
reverse: true, position: 'left', title: 'Volume', unit: 'shares',
},
},
navigation: { enabled: true, crosshair: true, navigator: true },

Both value axes support linear, log, symlog and signed sqrt; constant for symlog; min/max; reverse; left/right/none placement; title, unit, lineColor, labelColor and titleColor; tickFormat notation/fraction digits; and tickValues, tickCount, tickMinStep, tickMaxStep and tickSpacing. Price axes retain padded observed-price defaults. Volume axes use a zero baseline for signed linear/symlog/sqrt columns, clipped to authored bounds; logarithmic columns use the positive domain edge. Zero volume remains zero-height, and negative volume remains signed rather than becoming a fabricated positive bar. Logarithmic price/volume axes require positive complete observations in their own pane.

The five bound roles stay distinct within each pair, and volume fields remain distinct from all price fields. Fixed roles do not require seriesOverrides: volume defaults to secondary, and OHLC fields to primary. Explicit assignments matching those Y roles are accepted; Y swaps and named Y axes are rejected. Price rows can remain visible when volume is missing. Pane domains use full accepted data before X-window clipping. Full-source/synchronized Y metadata continues to describe prices only; author volume bounds to keep that scale fixed across remote detail frames.

Measured gutters take the larger label width when disjoint panes use the same side. Baselines remain aligned with each pane; labels within one pane still use the shared layout policies. Long rotated titles fit their own pane and retain the full accessible tooltip. The existing pane split is bounded for short hosts; authored pane ratios and arbitrary pane composition are not provided.

X navigation, calendar controls, the semantic brush, hover and reset apply to both panes. The navigator defaults to close prices; explicit generic navigator.series selection remains available. Shared tooltips use the price/volume units and keep all canonical measures. CSV windows, saved models and server-rendered SVG preserve source identities and raw values.

Value references/shapes select primary for prices or secondary for volume. Notes anchored to volume use its actual endpoint. Calculated references infer the bound volume role when axis is omitted. X references paint one segment in each pane. New drawings in the volume pane select secondary automatically; the editor can switch a shape between compatible pane axes. Clipped intersections remain decorative geometry and never become source observations.

Pivot exposes primary price and secondary volume forms, plus close-owner X selectors for paired collections. Gallery transitions clear incompatible fixed-role assignments; direct API updates reject them atomically. Pivot’s advanced resolvePivotChartSeriesPresentation and hasPivotChartSecondaryAxisData helpers accept optional bindings as their fourth argument so custom volume field IDs resolve correctly. Named X and paired collections with pane sizing are described below; additional Y/pane axes and secondary Y synchronization remain outside this contract.

rangeColumn, rangeBar, rangeArea, confidenceBand and errorInterval use the same bound-group axis contract in standalone Charts and Pivot. Parallel bindings.low / bindings.high arrays define range groups, with the existing single-channel broadcast policy. Error intervals additionally bind actual for their centers and retain their existing single-group channel limits. rangeInterval computes statistical sample intervals and is a different renderer; this section does not apply to it or timelineRange.

The bound high measure selects each group’s axes. Its low and actual fields follow those projections; a helper may explicitly select the same axes, but independently moving a bound or center is rejected. Unbound helper fields cannot select non-primary axes. For example, with all four fields mapped as numeric series:

bindings: {
low: ['forecastLow', 'volumeLow'],
high: ['forecastHigh', 'volumeHigh'],
},
axes: {
y: { scale: 'log', min: 1, max: 100, title: 'Price', unit: 'USD' },
yAxes: {
volume: { scale: 'sqrt', min: 0, max: 10000,
position: 'right', title: 'Volume', unit: 'shares' },
},
},
seriesOverrides: {
volumeHigh: { yAxis: 'volume' },
},
navigation: { enabled: true, navigator: true, crosshair: true },

Primary axes.y, secondary axes.ySecondary and named axes.yAxes[id] support linear, log, symlog and signed sqrt; positive symlog constant; authored min/max; reverse; placement; title, unit, lineColor, labelColor, titleColor; tickFormat.notation, minimumFractionDigits, maximumFractionDigits; and tickValues, tickCount, tickMinStep, tickMaxStep, tickSpacing. Axis IDs and definition limits follow the named-axis rules above. Empty declared value axes remain visible and provide references/shapes on the primary X frame. Automatic domains include zero for non-log scales and use only complete bound observations assigned to that axis, including error centers. Log scales require all complete bound values on that projection to be positive; an incomplete range stays a gap and its unrendered helper values do not invalidate the scale.

On horizontal rangeBar, logical value settings still belong to axes.y. Its primary physical X line/title/colors/position use axes.x, and category appearance uses axes.y, matching the horizontal bar contract above. Additional value axes use physical top/bottom/none placement; vertical range charts use left/right/none. Reversal changes the numeric direction without producing negative bar dimensions. Styled category, tick and title labels use measured gutters, including deterministic server rendering.

Range groups also accept axes.xAxes[id] and seriesOverrides[highId].xAxis. Vertical range types support category, time, linear, log, symlog and sqrt X; rangeBar supports categorical X. Category axes support path/data mapping, grouping and reversal. Time axes share UTC interval/missing-period policies; numeric X supports bounds, transforms, units, reversal, formatting and ticks. Named X navigation and brushes select navigation.xAxis and rangeSelection.xAxis independently; windows persist in viewport.x / viewport.xAxes[id]. The navigator, toolbar, gestures, hover and reset use the selected projection. Navigation and synchronization continue to control X and primary Y metadata; independently navigable secondary/named Y windows are not provided.

Ranges normalize inverted low/high bounds for geometry. Bars and bands clip to authored domains; incomplete rows split bands. A visible interval stem may remain when a bound or center is outside the domain, but a clipped intersection does not acquire a cap or endpoint marker. Area endpoint markers follow the raw bound high measure, including inverted ranges. Activations, tooltips, CSV and saved data retain the original values and field identities. Current-scope CSV cells follow each group’s X window; a broadcast helper exports the union of its group windows. Explicit navigator helper selections and synchronized peer tooltips use those same group identities; the compact overview has one shared value scale. Notes use the actual low/high/center endpoint and disappear when that endpoint is outside the domain or its range is incomplete. A broadcast helper can appear in several groups; its semantic note follows each drawn endpoint, while inferred helper formatting and calculated-reference axes use the first bound group. References and editable shapes use the selected axis; calculated references infer low/center axis assignments from their high owner when no axis is specified.

Pivot exposes the shared value/X forms and axis selectors on bound high measures. Gallery changes clear incompatible helper assignments; direct invalid updates remain atomic. Models and server-rendered SVG retain all supported axis options. Remote full-domain/sync Y metadata describes complete primary range groups only; author secondary/named bounds to keep their scales fixed across remote detail frames. Inverted-range styling, additional error-interval group channels, broader value-axis navigation remain separate capabilities.

boxPlot, violin and statistical rangeInterval support axes.y as their primary numeric value axis in standalone Charts and Pivot. This statistical interval type summarizes its bound matrix observations; it is distinct from the low/high-bound errorInterval and other range-group charts above.

axes: {
y: {
scale: 'log', min: 1, max: 100,
reverse: true, position: 'right', title: 'Sample duration', unit: 'ms',
tickValues: [1, 10, 100],
tickFormat: { notation: 'standard', maximumFractionDigits: 2 },
},
}

The primary value axis accepts linear, log, symlog and signed sqrt; positive symlog constant; min/max; reverse; left/right/none placement; title, unit, lineColor, labelColor, titleColor; tickFormat.notation, minimumFractionDigits, maximumFractionDigits; and tickValues, tickCount, tickMinStep, tickMaxStep, tickSpacing. Defaults retain the existing zero-inclusive linear domains. Category labels, numeric ticks and titles now use measured gutters, with deterministic server-rendered fallback.

Box and violin domains/positivity come from selected analyticalFrame.derived.distributions: actual samples or valid supplied summaries. Aggregate matrix cells are not substitutes for those source distributions. Summaries retain their quartile, whisker, outlier and optional known-mean semantics; violin summaries still require density. Statistical interval domains use the selected bindings.value matrix observations. Hidden box/violin series do not participate in the domain or log validation. Log axes require positive source observations/extrema; a KDE’s zero/negative kernel edge can be clipped to the positive display domain without invalidating otherwise positive samples.

Box plots retain chartOptions.orientation: 'horizontal'. Their numeric scale/bounds/reversal/ticks/unit stay in logical axes.y; primary physical value-axis line/title/colors/top/bottom/none placement use axes.x, while category line/title/colors/left/right/none placement use axes.y. Pivot exposes physical value/category forms in this orientation. Violin and statistical intervals remain vertical.

Bodies, whiskers, interval stems and density silhouettes clip to the authored value domain. Outside quartile boxes are omitted while visible whiskers/outliers can remain. A clipped stem intersection does not acquire a cap, median, mean or outlier marker. Visible statistical marks keep their original median/sample/summary inspection values; decorative intersections never become observations. Aggregate intervals remain read-only inspection targets rather than fabricated source activations, including a visible stem whose mean lies outside the domain. Density shapes interpolate their supplied/sample support at clipped edges without changing the source distribution.

Value/category references and editable shapes use registered statistical projections. Calculated references on box/violin series summarize their displayed per-category medians, not aggregate cells or a pooled sample population; interval calculated references retain their canonical source-field observations. Semantic notes on box/violin measures anchor to their displayed medians, including grouped box offsets, rather than to the aggregate matrix cell. Statistical interval notes use the selected source field’s actual observation along the interval. Out-of-domain endpoints are not annotated. A valid derived view with no statistics in the bound selection can retain authored empty axes; the existing requirement for a valid distribution view is unchanged.

Model save/restore and server-rendered SVG preserve these options. CSV retains the canonical matrix cells and is not rewritten into statistical summaries. Pivot uses the same parser and renderer and exposes the primary, secondary and named value-axis controls and per-series assignment. Histogram collections use their own bound-measure contracts; statistical analytical overlays remain unsupported. Categorical windows, grouping/reversal, brushes, navigator and synchronization are described in Statistical categorical navigation. Primary runtime Y hints can fix the value scale through the shared renderer.

Box plots, violins and statistical intervals also accept axes.ySecondary, named axes.yAxes, and seriesOverrides[id].yAxis. All value axes accept the numeric controls listed above and compute independent domains from their assigned source data. An undeclared secondary assignment creates the default secondary axis; other IDs must be declared in yAxes. Primary remains the default assignment.

axes: {
y: { title: 'Duration', unit: 'ms' },
ySecondary: { scale: 'log', min: 1, max: 100, title: 'Rate' },
yAxes: { cost: { position: 'right', title: 'Cost', unit: '$' } },
},
seriesOverrides: {
rate: { yAxis: 'secondary' },
cost: { yAxis: 'cost' },
}

Box/violin distributions occupy separate slots per selected series. Violin supports multiple selected series, series colors and visibility; a single uncolored series keeps its category palette. Statistical intervals accept two or more selected measures with no three-measure maximum. They aggregate observations within each assigned value axis, producing one interval per category and active value-axis group. Measures on the same axis share that interval slot. A one-observation group retains its source activation; multi-observation intervals remain aggregate inspection targets. Hiding a measure removes its observations before aggregation.

For horizontal box plots, additional value axes use top/bottom/none positions directly on ySecondary and each yAxes entry. The primary physical aliases described above remain in effect. Vertical statistical charts use left/right/none. Pivot clears incompatible additional-axis positions when orientation or chart type changes, while retaining scales, bounds and assignments. Direct incompatible configuration updates are rejected atomically.

References can target axis: 'secondary' or a named axisId; editable shapes use yAxis and notes retain the assigned series projection. Save/restore and SVG export retain all axis collections. Runtime full-domain/synchronization Y hints apply only to primary; author secondary/named bounds when those scales must stay fixed across source frames.

Box plots, violins and statistical intervals (rangeInterval) support the shared categorical viewport, navigator, semantic brush, hover and synchronization contracts. This also applies to horizontal box plots, whose logical X categories run along screen Y.

axes: { x: { scale: 'category', reverse: true,
groups: [{ level: 1 }, { level: 0 }] } },
viewport: { x: { scale: 'category', startId, endId } },
navigation: { enabled: true, navigator: true,
crosshair: true, tooltipMode: 'shared' },
rangeSelection: { enabled: true },

startId / endId are increasing canonical category IDs in dataset order, even when axes.x.reverse mirrors the screen. Clear viewport.x to restore all categories. Toolbar, wheel/pan/pinch gestures, action policies, navigator selection and reset reuse the general navigation controls. The semantic brush keeps its own persisted rangeSelection.range and callbacks; it does not replace the viewport or source data. Horizontal boxes use vertical plot gestures/brush geometry while the overview remains horizontal.

axes.x.groups uses the existing category-band level, size, tick and label styling contracts. Visible category labels, group spans, statistical bodies, note anchors and inverse coordinates share one ordered projection. Box group offsets and widths retain the full selected series layout when some series have no distributions in the current window. Source extrema and value scales remain full-frame while only the visible category statistics are painted. Visible calculated-reference scope uses that same window; out-of-window semantic notes are hidden. Current-scope CSV exports visible canonical rows with their original matrix values; all-scope CSV remains unchanged.

Box/violin navigator curves and shared/peer tooltip rows use the displayed per-category medians, not aggregate matrix placeholders. navigator.series selects bound distribution fields (or all); missing statistics remain gaps. Statistical interval overviews use actual bound observation fields, with the first eligible field selected by default. Compact overview values have a shared scale and never replace canonical datasets, callbacks or exports. An explicitly supplied separate overview with no distribution records retains its authored overview values; an overview with distributions projects their medians.

createChartSyncGroup can synchronize category X windows/hover and primary Y domains using the existing viewport, domain and hover policies. Category identities and numeric units/scales must be compatible. Primary statistical snapshots use selected sample/summary extrema, including violin support for its display domain, and authored Y bounds. They never infer a statistical domain from placeholder aggregate cells. Peer statistical readouts use the receiving chart’s own medians. Disconnect restores the local primary value scale through the shared sync lifecycle; navigation reset clears the current X window.

Categorical windows do not enable the viewport data-source API, which requires time or numeric X. Statistical charts now support those continuous scales and remote-detail requests as described in Statistical time and numeric X. Source distributions, counts and raw field identities retain their existing contracts.

Box plots, violins and statistical intervals also accept primary axes.x.scale: 'time' | 'linear' | 'log' | 'symlog' | 'sqrt'. Their default remains category. The mapped category key supplies the actual date or numeric X coordinate; labels and series positions are not substituted for that key. Numeric keys must be finite numbers, and log X keys must be positive. Time keys use the shared dates package’s ISO date, zoned instant and epoch-millisecond contracts.

axes: {
x: { scale: 'log', min: 1, max: 100, reverse: true,
unit: 's', tickValues: [1, 10, 100] },
y: { min: 0, max: 20, unit: 'ms' },
},
viewport: { x: { scale: 'log', min: 2, max: 80 } },
navigation: { enabled: true, navigator: true, crosshair: true },
rangeSelection: { enabled: true },

Numeric X supports positive symlog constant, authored min/max, reverse, unit, tickFormat.notation/minimumFractionDigits/maximumFractionDigits, tickValues, tickCount, tickMinStep, tickMaxStep and tickSpacing. Automatic X domains use actual full-frame X keys. Time X supports reverse and UTC interval: 'day' | 'week' | 'month' | 'quarter' | 'year', with the shared calendar/range-selector policies. Statistical marks are independent silhouettes/stems; missingPeriods does not add connecting segments between them. Missing dates retain their real elapsed spacing. Category group bands apply only to categorical X.

Marks, references, shape editing, hover, brushes, navigator, synchronization and CSV windows share the selected raw X projection. Authored viewport.x and rangeSelection.range use increasing raw min/max, including under reversal. Different X gaps retain different screen distances. Mark widths use the nearest distinct visible coordinate spacing so nearby distributions do not become broad ordinal slots. Value domains and statistical identities still use full accepted distributions/observations. Window clipping does not aggregate or rewrite samples, matrix cells or callback values.

Horizontal boxes support the same continuous X contracts along screen Y. Logical X scale/bounds/reversal/ticks/numeric formatting/units belong to axes.x, while logical Y values remain in axes.y. Physical appearance retains the established horizontal-box roles: axes.y owns the category line/title/colors and left/right/none placement; axes.x owns the primary value line/title/colors and top/bottom/none placement. For continuous categories, axes.x.unit describes numeric X and is not a primary-Y unit fallback. Author axes.y.unit for values. Legacy categorical fallback behavior remains unchanged. Pivot’s numeric X form edits the category’s logical numeric settings and physical Y appearance separately from the primary value form.

Time/numeric statistical charts can use the viewport data-source API. Detail responses must provide valid source distributions for box/violin views, plus matching raw fullDomain.x and optional primary fullDomain.y metadata. Separate overview requests retain their existing contract: distributions project to median curves, while explicitly authored overview values without distributions remain overview-only. Detail data, overview curves and source metadata stay separate. Calendar labels use @revolist/dates in browser and server rendering.

Save/restore and server-rendered SVG preserve continuous scale settings. Histogram collections use their own bound-measure contracts; statistical analytical overlays remain outside this contract.

Histograms retain equal-width categorical bins by default. Set axes.x.scale to linear, log, symlog or sqrt to place bin boundaries on a real numeric sample axis. X comes from the bound value measure (or x fallback), not category labels/keys or the optional aggregation measure. Bin membership and count/sum/mean metrics still use equal intervals in raw sample units; a nonlinear X transform changes their displayed widths and does not apply density or area normalization.

Numeric X accepts min, max, reverse, constant (symlog only), tickCount, tickValues, tickMinStep, tickMaxStep, tickSpacing, tickFormat (notation, minimumFractionDigits, maximumFractionDigits), unit, title, position: 'top' | 'bottom' | 'none', lineColor, labelColor and titleColor. Authored bounds clip existing bin rectangles without rebucketing or dropping source observations. Log X requires positive source samples; constant positive samples receive positive relative bin padding instead of crossing zero. At floating-point precision limits, indistinguishable bin boundaries are merged so every retained bin has a nonzero numeric interval and all source observations remain counted. Categorical X keeps its existing appearance controls; category reversal/grouping are not enabled for histogram bins.

Primary axes.y configures the aggregate metric: count, sum or mean, as selected by chartOptions.histogramAggregation. It accepts scale: 'linear' | 'log' | 'symlog' | 'sqrt', numeric bounds, reverse, symlog constant, the same numeric tick/format/unit controls and title, position: 'left' | 'right' | 'none', lineColor, labelColor and titleColor. Log Y validates nonempty bin metrics: positive counts remain valid over signed samples; nonpositive sums/means are rejected. Empty bins do not introduce zero-valued log observations. Metric baselines and endpoints clip to the authored Y domain. Large overflowing sums retain shared normalized geometry and raw-unit tick labels.

const axes = {
x: { scale: 'log', min: 1, max: 100, unit: 'ms', reverse: true },
y: { scale: 'sqrt', min: 0, max: 20, title: 'Bin count', tickValues: [0, 5, 20] },
};

Axes use measured gutters and preserve physical placement, save/restore and server-rendered SVG behavior. Pivot exposes the same X and primary-Y controls. CSV remains the canonical raw matrix; singleton bins retain their real observation identity and multi-observation bins remain aggregate inspections. Secondary/named histogram axes and explicit sample collections are supported; generic histogram analytical overlays remain separate.

Histograms now support local X zoom/pan, a navigator and persistent numeric range selection. Bins are computed from the full accepted source matrix before window clipping, so zoom does not rebucket observations or change bin counts/sums/means. Numeric X uses its configured transform/reversal; legacy axes.x.scale: 'category' (including the default) keeps its presentation and navigates in linear sample units. Histogram windows and brushes always use raw numeric sample coordinates, never source category IDs or generated bin IDs.

navigation: {
enabled: true,
navigator: { height: 64, chartType: 'steppedArea', color: '#6366f1' },
gestures: { wheel: true, pan: true, pinch: true },
actions: { zoomIn: 'enabled', zoomOut: 'enabled', reset: 'enabled' },
},
viewport: { x: { scale: 'linear', min: 20, max: 100 } },
rangeSelection: { enabled: true, range: { scale: 'linear', min: 30, max: 60 } },

Use navigation.enabled, navigator (boolean or appearance object), X action policies (zoomIn, zoomOut, panLeft, panRight, reset) and wheel/pan/pinch policies as on other ordered charts. The navigator uses bin centers in the active X transform and the selected count/sum/mean metric. Its chart type, color/monochrome, height and selection style customize that derived preview; series selection chooses among the explicitly bound samples. Overflowing sum previews use finite normalized geometric units. Navigator sample selection, per-sample overrides and existing overview color settings are supported; they style derived metric paths rather than replacing source measures.

viewport.x accepts the effective numeric scale and raw min/max; viewport: {} resets it. rangeSelection.enabled, range, style and range callbacks operate independently of the zoom window; brushes retain raw values under reversal. Missing/null sample rows have no active sample coordinate. Current-scope CSV filters raw sample rows by an explicit viewport, while dataScope: 'all' retains the canonical matrix, including missing sample rows. With no viewport, CSV keeps its existing raw-source behavior even when authored axis bounds clip the plot. A partially clipped bin still reports its full accepted-source metric; its current-scope CSV can contain fewer observations than that bin total. Navigation status counts source sample rows, not bins.

Synchronization shares raw sample X windows/domains and compatible primary aggregate-Y domains; default category presentation publishes linear X. The Y domain uses bin metrics rather than raw samples or helper weights. Overflowed sums whose Y domain is not representable do not publish a finite Y-domain claim. Aggregate-aware peer hover, shared tooltips and X crosshairs are described in Histogram shared hover and crosshair. Pivot exposes zoom, navigator, brush and readout controls. Save/restore and SVG export preserve X windows and selected ranges.

Independent Y windows/gestures, rectangle zoom, categorical-ID windows and generic histogram analytical overlays remain unsupported. Named histogram axes are supported by the collection contract. Remote histogram loading is supported through complete source frames; raw samples and complete bins are available through the histogram data view.

Set navigation.tooltipMode: 'shared' or navigation.crosshair: true for aggregate-aware histogram readouts on pointer motion and keyboard focus. The containing nonempty bin supplies its full accepted-source count, sample mean and configured count/sum/mean metric, including when the visible window clips that bin. Bins include their lower boundary and exclude their upper boundary; the final bin includes its upper edge. Empty intervals and samples outside the visible X domain clear the readout. A mean uses nonmissing values from the aggregation measure; a bin with no such values has no mean mark/readout and remains a gap in its navigator. Its X samples remain in the canonical data. These rules apply to legacy category presentation, all four numeric transforms, reversal and clipped windows.

The X crosshair follows the raw sample coordinate under the pointer. Keyboard inspection anchors it at the center of the bin’s visible projected interval. Singleton click/Enter activation still identifies its genuine source sample; repeated bins remain inspection-only. With both options off, ordinary datum/aggregate inspection keeps its existing behavior. rendererOptions.tooltip: false suppresses tooltip content while retaining an enabled crosshair and semantic X hover.

Local tooltip formatters receive optional context.aggregate (ChartHistogramTooltip, or PivotChartHistogramTooltip through the Pivot facade):

FieldMeaning
kindAlways 'histogram'.
binIndexIndex in the full bin partition, before viewport clipping.
start, endRaw sample boundaries.
countNumber of accepted source sample rows in the bin.
sampleMeanMean of the source X samples, independently of the aggregation measure.
aggregation'count', 'sum' or 'mean'.
scaledValue, multiplierThe computed metric is conceptually their product; both fields remain finite for overflowed sums.

context.value is the raw aggregate metric when representable, otherwise null. context.displayValue uses primary Y tick formatting and units, and formats an unrepresentable sum as a finite scaled value times a scientific multiplier. category is a genuine representative source member from the bin; series is the owning aggregation measure (or the sample measure for count). They provide provenance rather than describing the aggregate as one source cell. drillable is false and sharedItems is omitted. Mutating aggregate metadata or the supplied default rows does not change subsequent inspections.

rendererOptions: {
tooltip: {
formatter(context, defaults) {
if (!context.aggregate) return defaults;
return {
...defaults,
footer: `${context.aggregate.count} source samples`,
};
},
},
},
navigation: { tooltipMode: 'shared', crosshair: true },

The existing tooltip class, delay, follow-pointer, result validation and suppression policies apply. Returning null hides the local tooltip and crosshair. Aggregate summaries and metric names use the existing locale hooks (renderer.aggregateSummary and chartOptions.selectValues.count/sum/mean); textual content is escaped.

Compatible synchronized charts share raw X only for histogram hover. A histogram peer looks up its own bin and computes its own metric at that sample coordinate, even with different bin counts or after replacement. Incoming peer Y values do not replace histogram metrics. Peer readouts use the peer’s default localized summary, rather than invoking its local tooltip formatter. Clearing hover or destroying/replacing a chart clears the corresponding resources. Pivot exposes the existing shared-tooltip and crosshair controls. Shared hover does not change CSV, saved source cells or source activation payloads. Remote loading uses complete histogram source frames.

A singleton primary-axis histogram dataSource returns complete bin statistics in dataset.analyticalFrame.derived.histogram, alongside its loaded raw matrix rows. Explicit collections and nonprimary assignments use derived.histograms and plural source hints as described in the collection guide. Raw detail rows can cover one window or be empty; they are never used to estimate the missing bins. This typed ChartHistogramSummary is also accepted as precomputed client data and survives save/restore and server-rendered SVG. Pivot exports the same contract as PivotChartHistogramSummary.

The summary owns its partition and all three aggregation statistics. Changing chartOptions.histogramAggregation selects count/sum/mean from those statistics immediately. chartOptions.binCount is a source query hint for precomputed data, not a client rebucketing command. While a source request is pending or fails, the last accepted complete partition stays visible. Without an attached source, changing the hint does not alter a supplied summary’s boundaries.

Summary fieldContract
kind, scopeExactly 'histogram' and 'all'.
sampleSeriesId, valueSeriesIdExisting dataset series IDs matching bound value (or x fallback) and y (or sample fallback).
requestedBinCountThe source’s applied binCount hint, integer 0..256; 0 delegates automatic choice to the source.
aggregationApplied request mode: 'count', 'sum' or 'mean'. All three statistics are still supplied.
boundariesTwo to 257 finite, strictly increasing raw sample edges, giving at most 256 contiguous bins. Equal and irregular precomputed intervals are supported.
multiplierOptional common finite sum multiplier, at least 1; omitted means 1.
binsOne entry per boundary interval, in the same order. A nonzero hint cannot produce more bins than requested; precision coalescing can produce fewer.

Each bin contains:

Bin fieldContract
countNonnegative safe integer X sample count. The sum of all counts must also be safe.
sampleMeanFinite mean of accepted X samples within the interval; null for an empty bin.
valueCountNumber of nonmissing aggregation values among those X samples, from 0 through count.
sumFinite scaled sum: the conceptual raw sum is sum * multiplier. With no contributors, it must be 0.
meanFinite raw aggregation mean, or null when valueCount is zero. A missing mean stays a gap, rather than becoming zero.
representativeGenuine source ChartCategory, required for nonempty bins and omitted for empty bins. It may describe a source member outside the loaded matrix. Representatives must have distinct IDs and satisfy normal category/hierarchy validation.
// `dataset` contains only loaded raw rows, with sample/weight series headers.
// These category descriptors come from genuine members of the complete source.
dataset.analyticalFrame.derived = {
histogram: {
kind: 'histogram', scope: 'all',
sampleSeriesId: 'sample', valueSeriesId: 'weight',
requestedBinCount: 2, aggregation: 'sum',
boundaries: [1, 50.5, 100], multiplier: 1,
bins: [
{ count: 5, sampleMean: 16.6, valueCount: 5, sum: 15, mean: 3,
representative: firstSourceCategory },
{ count: 1, sampleMean: 100, valueCount: 1, sum: 6, mean: 6,
representative: lastSourceCategory },
],
},
};

Loaded X samples must fit the partition and cannot outnumber its global counts. Known loaded missing/nonmissing values constrain valueCount. A globally singleton bin activates a source datum only when its genuine sole sample is loaded; otherwise it remains aggregate inspection. A representative does not manufacture a raw row or an activation target. Metadata is bounded by maxDataPoints; global counts themselves do not allocate rows. Series subsetting drops a summary when either measure dependency is removed.

For histograms only, ChartDataSourceRequest.histogram adds a ChartHistogramSourceRequest (PivotChartHistogramSourceRequest in Pivot):

Request fieldMeaning
binCountEffective numeric hint, 0 for automatic; explicit boundaries set this to their interval count.
boundariesOptional raw sample edges from chartOptions.histogramBinBoundaries; responses must match exactly.
aggregationCurrent requested count/sum/mean mode.
scaleEffective numeric X scale. Legacy category presentation requests 'linear'.
constantEffective symlog constant; included only for symlog.

The existing purpose, viewport, bindings and pixelWidth fields remain unchanged. A custom createChartDataSourceController supplies the model’s histogram chartOptions through ChartDataSourceContext.chartOptions; the chart runtimes do this automatically. Histogram responses must match captured field IDs, bin-count and aggregation hints, and return fullDomain.x in the requested scale with endpoints exactly matching the outer boundaries. Log X requires positive global boundaries. Binding/transform changes that invalidate a stored summary need an atomic matching dataset update; the client does not reinterpret another measure’s statistics.

Within a source revision, global statistics, representatives and partition remain stable for the same fields, bin-count and X-transform request, across detail windows, reloads, pixel widths and aggregation modes. Advance dataSource.revision when that source truth changes. The controller checks retained signatures, validates before commit, and discards canceled late responses even when the provider ignores AbortSignal. Errors retain the last accepted frame. Cache/signature storage stays bounded.

fullDomain.y is optional. It applies only to the summary’s applied aggregation; another selected mode derives its Y domain from its own global statistics. Authored bounds still win over source hints, while transient synchronization retains its usual precedence. The complete view already supplies navigator statistics, so chart runtimes issue detail requests without a separate histogram overview load. Direct controller overview responses must satisfy the same complete-view contract.

CSV and data tables retain loaded raw rows only, including dataScope: 'all'; that option does not fetch or reconstruct unloaded source records from totals. Saved models retain loaded rows plus the complete summary. The navigation status distinguishes visible/total bin intervals, visible loaded samples and global source count; customize it with localeText.navigation.histogramWindowSummary(visibleBins, totalBins, loadedSamples, totalSamples).

Both standalone Charts and createStandalonePivotChart accept dataSource. The standalone Pivot adapter delegates source work to the shared Charts runtime and adds no grid or Pivot query. Their refs expose setDataSource(source | undefined) and reloadData(); source handles/functions are not serialized. Detaching a source keeps its last accepted summary as ordinary precomputed data. Mounted Pivot chart refs use the same runtime source lifecycle.

Irregular intervals keep raw count/sum/mean heights by default; enable area encoding to divide heights by their raw widths.

Set chartOptions.histogramBinBoundaries to an array of 2..257 finite, strictly increasing raw sample edges. This defines at most 256 contiguous bins, including irregular widths, and overrides binCount. Omit the option or use [] to restore automatic/count-based binning. Duplicate, descending, nonfinite or single-edge lists are rejected. Explicit edges must contain every finite bound X sample; out-of-range samples cause an atomic update error rather than disappearing from the totals. Logarithmic X also requires positive edges.

chartOptions: {
histogramBinBoundaries: [0, 2, 10, 100],
histogramAggregation: 'sum',
},

Intervals include their lower edge and exclude their upper edge, except the final interval includes the final endpoint. Counts, sums and means retain their normal raw metric units; unequal-width area/density normalization is not implied. Empty intervals retain their place in the partition. Missing X samples stay in the raw dataset but contribute to no bin. With no loaded samples, explicit edges still define an empty partition. Preview and navigator projections preserve the supplied partition. Numeric axes paint the actual interval widths; legacy category presentation retains its existing bin labels and sample-coordinate navigation.

Bounds, zoom and brushes clip this stable partition without rebucketing. CSV continues to export canonical raw rows, with current-scope filtering by the raw sample window. Save/restore and SVG rendering preserve the edges. Standalone Pivot accepts the same option. Its Bin boundaries appearance field edits a comma-separated list, validates before committing, and supports empty/reset for automatic binning. Labels and help text are localized by localeText.chartOptions.labels.histogramBinBoundaries and localeText.chartOptions.descriptions.histogramBinBoundaries. Schema consumers can recognize its ChartNumberListOptionDefinition with control: 'numberList', array defaultValue, and the existing key field; its empty default means automatic binning.

For a histogram dataSource, the request includes optional histogram.boundaries when explicit edges are configured. Its effective histogram.binCount is then boundaries.length - 1, regardless of the authored automatic binCount. Responses must return exactly those edges and that effective requested count. They remain part of cache/cancellation and stable-source identity. While a new request is pending or fails, the last accepted complete summary remains visible. Precomputed summaries continue to own their own partition; without an attached source, this option does not reinterpret or rebucket their statistics. See complete source frames for every summary/request field.

Set chartOptions.histogramArea: true to divide each bin’s count, sum or mean by its full raw sample interval width. The default is false, preserving ordinary metric heights. This works with automatic bins, explicit histogramBinBoundaries, and complete precomputed/source summaries. It does not change the partition or raw statistics.

chartOptions: {
histogramBinBoundaries: [0, 2, 10, 100],
histogramAggregation: 'sum',
histogramArea: true,
},
axes: { x: { scale: 'linear', unit: 's' }, y: { unit: 'kg' } },

A sum of 12 kg over a 4 s interval has height 3 kg / s. On linear X/Y axes with the full bins visible, rectangle areas are proportional to their aggregates. Area mode removes bar gaps and corner rounding to preserve that relationship. Nonlinear transforms and viewport clipping change visible screen areas; density always divides by the original raw width, never a transformed or clipped width. This is metric density, not probability normalization: signed sums/means remain signed, counts keep their original totals, and a missing mean remains a gap.

Y bounds/ticks/transforms/reversal and navigator values use the density heights. axes.y.unit remains the raw aggregate unit; displayed density units are derived as Y / X, with 1 for an unspecified numerator and symbolic x for an unspecified sample unit. In the example, Y ticks show kg / s. The sync snapshot uses that derived unit, keeping density domains distinct from raw aggregate domains. Authored Y bounds are interpreted in density units. Source fullDomain.y describes the raw aggregation and is ignored in area mode; density domains derive from all complete bin statistics, with authored bounds and compatible transient sync retaining precedence. Toggling area encoding is client-side and does not require a different source query or source revision.

Tooltips retain the original aggregate value, scaledValue and multiplier, including existing overflow metadata. Area mode adds optional ChartHistogramTooltip.density: number (also PivotChartHistogramTooltip.density) with the finite plotted height. ChartTooltipContext.value and displayValue continue to describe the raw aggregate. Default bin tooltips add a metric-per-sample row, even without shared/crosshair mode; customize its label with localeText.renderer.histogramDensity. Genuine singleton click/Enter activation still carries the original observation; aggregate bins remain inspectable without invented rows.

Pivot exposes the same boolean as Encode aggregate by bin area. Localize it through localeText.chartOptions.labels.histogramArea and localeText.chartOptions.descriptions.histogramArea. Save/restore and SVG/image exports preserve area encoding; raw datasets and CSV remain unchanged. Ratio arithmetic handles overflowing finite interval spans and normalized sums when the resulting density is representable. If a nonzero density overflows or underflows the finite JavaScript number range, the option/data update fails atomically instead of painting a false finite height. Raw totals can still use the existing scaled-sum metadata when area mode is off.

Use createChartHistogramDataView from @revolist/charts or @revolist/charts/core to inspect the same partition and aggregates as the renderer, without reading SVG or fabricating raw rows. It accepts the histogram model’s chartType, dataset, bindings, chartOptions, axes and viewport; chartType must be 'histogram'. Normal dataset, option, binding, axis and viewport validation applies. The returned snapshot owns its arrays and category descriptors, so changing it does not change the chart.

import { createChartHistogramDataView } from '@revolist/charts';
const current = createChartHistogramDataView(chart.model);
const loaded = createChartHistogramDataView(chart.model, { dataScope: 'all' });
console.table(current.samples);
console.table(loaded.bins);

The optional second argument is ChartHistogramDataViewOptions: dataScope: 'current' | 'all', default 'current'. Current scope filters samples by the committed raw X viewport and includes bins intersecting that window, with their full statistics. Raw samples at a window endpoint are included; the containing bin is retained even if its rectangle has no visible width at that endpoint. Without an explicit viewport, all loaded rows/bins remain in the view; authored axis bounds alone clip painting. All scope includes every loaded matrix row and every accepted bin. Neither scope loads or reconstructs missing source records. These sample-window rules are shared with raw CSV, including explicit partitions extending beyond loaded sample extrema.

ChartHistogramDataViewInput is the model-field input type. ChartHistogramDataView contains:

FieldMeaning
dataScopeEffective current/all scope.
sampleSeriesId, valueSeriesIdBound sample and aggregation series IDs; absent when there are no corresponding series headers.
aggregation, areaEffective count/sum/mean and area-encoding flag.
loadedRowCountAll loaded canonical rows, including missing X samples, before scope filtering.
loadedSampleCountAll loaded finite bound X samples, before scope filtering.
sourceSampleCountComplete accepted sample count from bins, including unloaded source members.
samplesScope-selected ChartHistogramSampleRow entries in canonical matrix order.
binsScope-selected ChartHistogramBinRow entries in accepted partition order.

Each ChartHistogramSampleRow has category (genuine source descriptor), categoryIndex (original matrix row index, never renumbered), sample (raw X or null), value (raw bound aggregation value or null) and binIndex (zero-based accepted bin index, or null for missing X). A row with missing X remains inspectable in the no-window/all view, but contributes to no bin. Row values are not replaced with counts or densities.

Each ChartHistogramBinRow has:

FieldMeaning
index, start, endStable zero-based bin index and full raw edges.
endInclusiveTrue only for the final accepted bin. Every bin includes its start; other ends are exclusive.
count, loadedCountComplete source sample count and number of loaded finite samples in the full bin, before window filtering.
sampleMeanComplete X mean, or null for an empty bin.
valueCountComplete nonmissing aggregation contributor count, independent of the selected aggregation mode.
scaledValue, multiplierRaw aggregate represented as their product, including overflowing sums.
valueFinite raw aggregate; null for an unrepresentable sum or a mean with no contributors. Use aggregation/contributor/scaled metadata to distinguish those cases.
densityOptional finite plotted height with area encoding; absent when area is off or the mean is missing.

Complete source summaries therefore expose global bin counts/metrics beside the loaded rows, including when the loaded matrix is empty. They do not manufacture unloaded samples or activation targets. Density, signed/missing values and scaled sums retain the area encoding and complete source frame contracts.

Pivot exports the same helper as createPivotChartHistogramDataView, with aliases PivotChartHistogramDataViewInput, PivotChartHistogramDataViewOptions, PivotChartHistogramDataView, PivotChartHistogramSampleRow and PivotChartHistogramBinRow, through both @revolist/pivot and @revolist/pivot/charts. The adapter uses the shared Charts snapshot directly.

In Pivot’s histogram Data tab, Samples retains the native read-only grid, existing sorting/filtering/selection configuration and canonical measure columns. It adds numeric Bin, Bin start and Bin end columns; displayed bin numbers are one-based. Bins shows a bounded, scrollable table of complete intervals, source/loaded counts, means, contributors, raw/scaled aggregate and optional density. Switching views does not change the chart model, dataset or CSV. Bins are an inspection table, not additional source rows. The existing dataView UI policy hides/disables this view and its controls; sample-grid settings still use dataGrid.

Localize the view through localeText.histogramData. Its string fields are viewLabel, samples, bins, bin, start, end, count, loadedCount, sampleMean, valueCount, metric, density and interval. Its summary(loadedRows, loadedSamples, sourceSamples) callback reports the full loaded-frame/global totals independently of the current scope. Existing dataTable captions, empty-value/sort labels and renderer number/axis formatters still apply. UI view choice, sorting and filtering are local presentation state; save/restore retains the chart settings and data rather than table state.

Heatmap X denotes series/columns and Y denotes categories/rows. Their categorical direction settings live in axes.matrix, independently of numeric value-axis definitions:

axes: {
matrix: {
columns: { reverse: true },
rows: { reverse: true },
},
},

ChartMatrixAxes has optional columns and rows objects. Each is a ChartMatrixAxis with optional boolean reverse, default false. Columns normally run left to right and rows top to bottom; each true mirrors that dimension’s projection. The settings apply to cells, category/series labels, aggregate bucket extents, hover/crosshair points, pointer gestures and the full-matrix navigator. Dense/partial buckets keep their original memberships, counts, means and source provenance.

Viewport x/y still uses inclusive canonical series/category IDs with scale: 'category'. Reversal never reorders matrix cells, renumbers IDs, changes CSV/data-table order or reverses saved endpoints. Missing endpoints remain dormant and reactivate when their IDs return. Sync groups retain canonical identities, so compatible peers may use different directions. Navigator start/end handles retain their semantic endpoint roles as physical positions mirror; their keyboard increments follow canonical row/column order. Pointer dragging uses the reversed projection to resolve those same IDs.

Standalone Charts and Pivot share the parser and renderer. Save/restore, SVG/image export and navigator previews preserve directions. Pivot’s Customize → Axes and scales exposes Reverse heatmap columns and Reverse heatmap rows. Locale strings are localeText.chartOptions.matrixColumnsReverse and localeText.chartOptions.matrixRowsReverse; UI policy keys are axes.matrix.columns.reverse and axes.matrix.rows.reverse. Gallery changes to another chart family clear matrix-only settings. Pivot exports PivotChartMatrixAxes and PivotChartMatrixAxis aliases through its root and charts facade.

This matrix configuration is currently heatmap-only. It also supports physical placement, titles, colors and measured gutters as described below. Numeric row/column scales/bounds, additional matrix axes remain separate axis work. The cell-value color scale remains independent of row/column direction. Calendar heatmaps retain their calendar layout. General semantic XY brushing also remains separate from viewport navigation.

Configure physical heatmap axes through axes.matrix.columns and axes.matrix.rows. Each ChartMatrixAxis supports these fields, also exposed through the equivalent Pivot types:

FieldMeaning and default
reverseIndependent categorical direction; default false.
groupsOptional inner-to-outer categorical group bands; omitted/empty retains leaf captions. See grouped heatmap axes.
positionColumns: 'top' (default), 'bottom' or 'none'. Rows: 'left' (default), 'right' or 'none'. Physical placement is independent of reversal.
titleOptional string, at most 4096 characters. Overrides the corresponding legacy presentation.xAxisTitle/presentation.yAxisTitle fallback. An explicit empty string suppresses that fallback.
lineColorShared authored hex-color vocabulary; defaults to the theme’s muted axis color.
labelColorRow/column label color in the same hex vocabulary; defaults to theme muted text.
titleColorTitle color in the same hex vocabulary; defaults to theme foreground text.
axes: {
matrix: {
columns: { position: 'bottom', title: 'Sample columns', lineColor: '#445566' },
rows: { position: 'right', title: 'Source categories', labelColor: '#334455' },
},
},

position: 'none' removes that axis’s line, labels, title and measured gutter. Matrix cell geometry, source identity, reversal, hover, windows and navigator remain active. Titles and colors affect decorations, independently of the numeric cell-value color mapping. Source data and CSV order stay canonical.

Heatmaps use the shared measured-axis layout. It measures final label/title text, renderer font styles and formatter results before laying out the cells; hidden axes release their space. Row/column label regions fit their allocated gutters/slots, preserving full accessible captions when text is truncated. Gutters are bounded to keep a usable plot on narrow hosts. Browser measurements use SVG text metrics; server-rendered SVG uses deterministic font-metric fallback and retains full captions in accessible labels/titles. Navigator and pointer geometry uses the resulting measured plot bounds. Overview previews retain categorical direction with decorations omitted.

Existing renderer label/element overrides retain precedence over axis colors. Matrix row labels have kind: 'category', axis: 'y', their genuine category and original categoryIndex. Column labels have kind: 'category', axis: 'x', genuine series and seriesIndex. Titles use kind: 'axisTitle'. Formatter results are reused between measurement and painting per semantic source identity, including when several source labels have identical text. No synthetic numeric row/column values enter formatter provenance.

Pivot reuses its physical axis forms for Heatmap columns and Heatmap rows, exposing position and title alongside the existing reversal controls. Numeric scale, bound, tick and unit controls are omitted for these categorical axes. Configure colors through the API or existing renderer styles. Localize the group captions with localeText.chartOptions.matrixColumnsAxis and localeText.chartOptions.matrixRowsAxis; common axisPosition, axisTop, axisBottom, axisLeft, axisRight, axisHidden, axisTitle and axisField locale hooks still apply. UI policy keys are axes.matrix.columns.position, axes.matrix.columns.title, axes.matrix.rows.position and axes.matrix.rows.title. Their axes.matrix.columns/axes.matrix.rows parents and the existing axes policy also apply.

Save/restore and SVG/image exports retain these settings. Queued Pivot updates own matrix appearance before validating the resulting chart type; gallery transitions to another family clear matrix-only settings. Numeric matrix row/column scales/bounds, additional matrix axes remain separate work. Calendar heatmaps continue to use their calendar layout.

axes.matrix.columns.groups and axes.matrix.rows.groups accept up to eight ChartCategoryAxisGroup objects, ordered from the innermost band next to the cells to the outermost band. A nonempty list replaces that dimension’s ordinary leaf captions; include the leaf path level as the first group when both leaf and parent labels are needed. An omitted or empty list retains ordinary captions.

axes: {
matrix: {
columns: {
position: 'bottom',
groups: [
{ values: { revenue: 'Revenue', costs: 'Costs' }, size: 32 },
{ values: { revenue: 'Financial', costs: 'Financial' }, size: 32 },
],
},
rows: { position: 'right', groups: [{ level: 1 }, { level: 0 }] },
},
},

The lookup keys in the example are stable series IDs, not labels or row indices. Row groups use stable category IDs. Each group supports:

FieldBehavior
levelZero-based path index, an integer from 0 through 127. Defaults to the final member of the row category’s path or column series’ columnPath. Corresponding labels supply display text.
valuesOptional lookup of stable IDs to string, finite number, boolean or null, at most 100,000 entries. Missing IDs fall back to the chosen path level. Authored values supply their own text; null uses the localized missing-value caption.
sizeBand thickness, 16 through 160 SVG pixels. Defaults to 32 for columns and 80 for rows. Shared measured layout bounds total gutters to retain a usable plot; bands can shrink and captions can truncate or be omitted on small hosts.
tickSizeBoundary tick length, 0 through 24 pixels; default 6, capped by the fitted band thickness.
lineColor, labelColorExisting authored hex-color vocabulary. Each band overrides the matrix axis color; absent overrides use the axis color and then the renderer theme. Renderer label styles/formatters retain their existing final precedence.

Groups merge only adjacent members with the same typed value and outer-group identities. Numeric 1 and string '1' remain separate even when their captions match. Repeated nonadjacent groups remain separate. Windowing clips groups to visible canonical members; reversal mirrors their physical projection without changing IDs, cell values or CSV. Group boundaries use source row/column positions, independently of dense cell bucket aggregation. Navigator previews omit decorations and preserve the full-matrix overview.

Both physical sides, titles, hidden axes, save/restore, server SVG and image export use the shared axis pipeline. position: 'none' hides all bands and releases their gutter. Grouping does not aggregate, reorder or add data. Persisted group definitions contain data, without callback functions. Runtime matrix grouping callbacks are described below; additional matrix axes remain separate work.

Pivot Customize → Axes and scales reuses the group editor under Heatmap columns and Heatmap rows. Enable Group category labels to initialize up to eight levels from that dimension’s path depth, then edit levels, add or remove bands. When columns contain measures without a column dimension path, enabling grouping initializes an explicit stable series-ID-to-label lookup so each measure retains its caption. Path-only API groups on an empty column path have a missing group value and no invented path caption. Explicit values lookups are API-configured and disable the corresponding path-level editor. These controls use axes.matrix.columns.groups and axes.matrix.rows.groups UI policy keys plus existing parents. Existing chartOptions.categoryGroups, categoryGroupLevel, categoryGroupAdd, categoryGroupRemove and axisField locale hooks apply; accessible field labels include the heatmap dimension name. No new locale strings are required.

Use ChartsRendererOptions.matrixAxes.rows.groups and matrixAxes.columns.groups to derive group values or captions from source metadata. Each dimension independently overrides its persisted axes.matrix group list. An explicit empty array disables grouping for that dimension and restores ordinary leaf captions; an omitted groups keeps the persisted list. Runtime definitions replace the whole list, including its sizes/styles, without merging by index.

import { createChartsRenderer } from '@revolist/charts';
const renderer = createChartsRenderer({
matrixAxes: {
rows: {
groups: [
{ getValue: category => ({ value: category.id, label: category.label }) },
{ getValue: category => category.path[0] ?? null },
],
},
columns: {
groups: [
{
size: 70,
getValue: (series, seriesIndex) => ({
value: series.measure?.id ?? series.id,
label: `${seriesIndex + 1}: ${series.measure?.label ?? series.label}`,
}),
},
],
},
},
});

ChartCategoryAxisRuntimeGroup is the row type: getValue(category, categoryIndex) receives a read-only ChartCategory and its original index in the canonical accepted dataset. ChartSeriesAxisRuntimeGroup is the column type: getValue(series, seriesIndex) receives a read-only ChartSeries with columnPath, labels and optional measure metadata, plus its original index in the canonical accepted dataset. Columns are genuine series, without fabricated category paths. Windows and physical reversal preserve these indexes and IDs. For remote sources, indexes refer to the accepted frame, without inventing positions or members for unloaded records.

Each runtime group accepts all persisted group fields (level, values, size, tickSize, lineColor, labelColor) plus optional getValue. Without a callback, that group’s own stable-ID lookup/path configuration supplies values and labels. A callback returns a string, finite number, boolean, null, or { value, label } with a scalar value and string label. Scalar results display their string form; scalar null uses localeText.renderer.missingValue. A value/label pair separates typed grouping identity from its caption. Adjacent values merge only when both their type and all outer group identities match. undefined, nonfinite numbers and malformed pairs are invalid; invalid configuration/results throw TypeError, and callback exceptions propagate. The eight-band and existing size/tick/color limits still apply.

The renderer caches each visible member’s result by stable ID per group, per render. Measurement and painting share those results; it does not call a row callback once for every matrix cell. A new render after data, viewport, resize or chart updates starts a fresh cache, including when IDs remain the same. Keep callback input read-only. Hidden axes and navigator previews do not evaluate callbacks. Runtime matrix options are used only by heatmaps; other chart families ignore them. Hidden-axis group configuration is still validated.

All band projection, measured gutters, physical sides, reversal, caption fitting and renderer style/label precedence remain shared with persisted grouping. Callbacks do not aggregate/reorder source cells or affect CSV. Runtime functions and derived captions are not serialized into the model; configure the same renderer when restoring a chart. Standalone Charts accepts the options through rendererOptions or createChartsRenderer. Pivot uses createPivotChartsRenderer({ matrixAxes: ... }) and its public renderer option. Pivot exports PivotChartCategoryAxisRuntimeGroup and PivotChartSeriesAxisRuntimeGroup through both its root and charts facade. The existing Pivot group forms edit persisted groups; renderer runtime overrides keep their normal precedence.

Server rendering accepts the same configuration through renderChartToSVG({ model, width, height, rendererOptions: { matrixAxes: ... } }). It evaluates callbacks using the server model’s real metadata and deterministic text fitting. SVG/image output includes the resulting labels; saved models still contain only persisted settings. Reapply matching renderer options for client hydration. No additional locale or UI policy keys are introduced by these renderer callbacks.

Both columnLine and areaLine support:

OptionDefaultBehavior
chartOptions.normalizeStacksfalseNormalize explicitly grouped column, area and line layers independently for each category, X axis, Y axis and stackGroup. Ungrouped scalar layers, scatter and range layers keep raw values; column/path stack families are independent.
chartOptions.normalizedTo100Finite target from 1 through 1,000,000. Used by normalized groups only; changing it alone does not enable normalization.
chartOptions: { normalizeStacks: true, normalizedTo: 100 },
seriesOverrides: {
revenue: { type: 'column', stackGroup: 'financial', yAxis: 'primary' },
costs: { type: 'column', stackGroup: 'financial', yAxis: 'primary' },
volume: { type: 'line', yAxis: 'secondary' },
},
axes: { y: { title: 'Share' }, ySecondary: { title: 'Raw volume' } },

Positive and negative contributors have independent totals, so each nonempty sign reaches +normalizedTo or -normalizedTo. Nulls do not contribute; zero creates a zero-height segment at the current positive stack endpoint. Hidden series are excluded from denominators, and different named X/Y axes or group IDs never share a denominator. Calculations rescale overflowing raw totals to keep representable proportions finite. Final nonzero-sign endpoints are pinned to the target to avoid rounding outside the axis; underflowing shares retain their original sign and may have zero visible height. Source values, category/series IDs, clicks, tooltips, data labels and CSV remain raw; normalization changes plot-space endpoints and axis domains.

An axis containing only normalized grouped columns uses the corresponding sign domain (0..target, -target..0, or -target..target). An empty/all-zero pure stack axis retains 0..target. Its ticks display percentages when the target is 100, unless an authored unit/tick format supplies formatting. Other targets use numeric ticks. If raw and normalized layers share a value axis, its numeric domain includes both; use separate Y axes to compare their units clearly. This decision considers all layers, including layers on other named X axes. Existing authored bounds, scales, reversal and clipping still apply, and bounds must form a valid domain. Existing logarithmic-column and error-bar/stack compatibility restrictions remain.

Analytical overlays and calculated references use each normalized column component in axis units; raw layers retain raw values. Derived overlays may expand automatic bounds as usual, while explicit bounds remain authoritative. Public resolveChartAnalysis callers supply chartType, seriesOverrides and chartOptions in its final options argument to use matching normalized semantics. Peer points use normalized stack endpoints. Primary-axis synchronization uses a normalization-specific unit descriptor that includes the target, authored unit and whether the axis mixes raw units, preventing accidental Y synchronization with raw axes or a different target. Raw remote fullDomain.y hints are ignored for a primary axis containing normalized columns, because their units differ; explicit authored Y bounds remain effective. Denominators are calculated from each accepted category’s visible group values. Providers must return complete contributing group values for those categories; omitted remote contributors cannot be reconstructed from a raw domain hint.

Navigator previews show each selected column’s normalized component, using complete visible chart-group denominators before preview series selection. Selecting one preview series does not rescale that series to 100. Navigator-only hiding/selection does not change the main chart’s denominator. Other preview layers retain their normal raw values; preview style selection does not turn the overview into a full stacked composition.

Save/restore, server SVG and image export preserve normalization settings. When updating chartOptions, supply the complete desired object (for example { ...chart.model.chartOptions, normalizedTo: 10 }), since it is a replacement. Disabling normalizeStacks restores raw stack endpoints without changing the source matrix.

Pivot uses the shared schema in Customize → Chart appearance: Normalize stacks and Normalize to, with existing option reset behavior. Localize them through localeText.chartOptions.labels.normalizeStacks, descriptions.normalizeStacks, and existing labels.normalizedTo/descriptions.normalizedTo; formatValue, resetOption and reset-all hooks also apply. UI policy keys are chartOptions.normalizeStacks and chartOptions.normalizedTo plus existing parent policies. No separate Pivot math or options contract is required. Bubble/histogram/polar combinations remain separate composition work.

In columnLine and areaLine, seriesOverrides[id].stackGroup now supports type: 'area' and type: 'line' as well as columns. Area and line layers in the same group share cumulative endpoints; canonical series order is the default, configurable through stack ordering below. An area fills between its own cumulative baseline and endpoint; a line traces its cumulative endpoint. Each positive and negative sign has its own accumulator. Layers without an explicit group retain their existing raw geometry.

seriesOverrides: {
baseline: { type: 'area', stackGroup: 'traffic' },
extra: { type: 'area', stackGroup: 'traffic' },
forecast: { type: 'line', stackGroup: 'traffic' },
revenue: { type: 'column', stackGroup: 'traffic', yAxis: 'secondary' },
},
chartOptions: { normalizeStacks: true, normalizedTo: 100 },

Column stacks and path stacks are independent families, even if they reuse the same group string and axes. Line and area layers may share a path stack. X-axis ID, Y-axis ID and group string further partition each family. Column slots are unaffected by path groups. zIndex changes paint order and overlap visibility, while stack accumulation follows the configured stack order independently. Hidden layers do not contribute; missing contributions do not reset other layers’ accumulators at that category.

The existing chartOptions.normalizeStacks toggle applies to every explicit supported stack family. Positive and negative path contributions normalize independently to ±normalizedTo, just like columns. normalizedTo still defaults to 100 and accepts finite values from 1 through 1,000,000. Raw source tooltips, clicks, data labels, CSV and model matrices retain original values; peer points and path geometry use cumulative endpoints. Analytical overlays, calculated references and navigator previews use normalized component values when normalization is enabled. Selecting a single preview series does not change its full visible-group denominator. Reversal, category/time/numeric X, named axes, authored bounds, SVG/SSR and save/restore use the common pipeline.

With default null handling, stacked area fills and their top strokes break at null/calendar gaps and at changes between positive and negative contributions, so they do not bridge unrelated sign baselines. Stacked line traces connect finite cumulative endpoints across sign changes, while nulls (unless connectNulls is enabled) and configured calendar gaps still break the trace. One-category runs retain their source marker even when the area has no visible width. Default linear area fills follow the retained envelope; authored per-series curves interpolate both top and baseline. Sign changes do not reorder or synthesize source observations.

Dense combination stacks use per-layer envelope sampling under sampling: 'auto'. Explicit m4, minmax, or lttb coordinates one bounded source selection across all cumulative edges in each axis/group partition. M4/minmax retain extrema in their selected buckets; LTTB approximates shape. Finite/calendar runs stay separate and category indexes remain canonical. sampling: 'none' retains all accepted path observations. Source rows and CSV are never sampled. Scalar scatter and bound range-area layers cannot join these stack groups; existing error-bar/stack restrictions remain. Positive stacked lines may use logarithmic value axes; zero/negative source observations remain invalid there, and normalized log domains use a positive lower limit. Existing logarithmic column/combination-area restrictions remain. Nonlinear scales change screen proportions as usual.

Pivot reuses the existing series layer and stack group controls under Customize → Axes and scales. Group fields appear for column, area and line layers. Switching among those types retains a group; switching to scatter, range-area or a helper clears it. The shared Normalize stacks and Normalize to controls remain in Chart appearance. Existing localeText.chartOptions.stackGroup, seriesLayer and labels.normalizeStacks/descriptions.normalizeStacks locale hooks apply, and existing axes.seriesLayer, chartOptions.normalizeStacks, chartOptions.normalizedTo UI policies still apply. No extra saved option or Pivot-specific calculation is introduced.

Stack baselines are selected separately with chartOptions.stackOffset and seriesOverrides[id].stackOffset: diverging preserves current defaults, none accumulates algebraically, and expand uses algebraic unit totals. Expansion replaces percentage preprocessing for its partition. See baseline policies, normalization precedence and raw source behavior.

chartOptions.stackOrder, default 'none', controls accumulation within each stack. It is available on groupedColumn, stackedColumn, normalizedColumn, groupedBar, stackedBar, normalizedBar, line, smoothLine, steppedLine, area, steppedArea, stackedArea, normalizedArea, columnLine and areaLine. On grouped bars/columns it takes effect when stack groups are active; unstacked geometry is unchanged.

PolicyAccumulation order
'none'Canonical series order.
'reverse'Reverse canonical series order.
'ascending'Increasing signed sum of raw values over the accepted dataset; equal totals retain source order.
'descending'The exact reverse of ascending order, including ties.
'appearance'Earliest canonical category index at the series’ maximum finite raw value. Equal peak indexes retain source order; all-missing series follow observed ones.

Null/missing values do not contribute to sums or peaks; a series without contributions has total zero. Total comparisons preserve finite Number values across overflowing sums, subnormals and cancellation. Ordering uses raw values before normalization or overflow scaling. Appearance means source category index, independently of physical X position, sorted numeric/time keys or axis reversal.

chartOptions: { stackOrder: 'ascending' },
seriesOverrides: {
revenue: { type: 'column', stackGroup: 'financial', stackOrder: 'reverse' },
costs: { type: 'column', stackGroup: 'financial' },
count: { type: 'line', yAxis: 'secondary' },
},

Optional seriesOverrides[id].stackOrder selects a stack’s policy instead of the chart-wide default. One member can configure the stack; other members may omit it. Multiple authored policies on active members of the same stack must agree, or the update rejects before commit. An explicit 'none' overrides a global automatic policy; removing the property restores the global fallback. Hidden members do not contribute values or conflicting policies.

Each X-axis/Y-axis/group partition orders independently. Native paths partition by their assigned axes and explicit stack groups. Native bars/columns also respect explicit/default stack groups. Combination column and area/line families remain separate even when their group strings match. Scatter/range/helper layers do not form ordered stacks. An ungrouped combination layer remains unstacked. Regular native line/area paths retain dormant policies while ungrouped; other unsupported types reject the option.

Ordering changes cumulative baselines/endpoints, including source marker locations, without reordering canonical data, colors, legend items, IDs, click payloads or CSV. Positive and negative accumulation remains independent. Column group slots retain their original positions. Combination zIndex still controls paint order separately. Native overflow detection follows the actual order, retaining finite geometry even when reordering exposes floating-point overflow.

Policies use all categories in the accepted frame, before viewport clipping or path sampling. Zooming/reversing the same frame does not recalculate from just the visible window. A new data frame, visibility change or member policy can change automatic order. Remote partial frames provide only their accepted observations: fullDomain is not a source of global series totals/peaks. Providers needing stable global automatic ordering must supply the relevant full observations, or provide a stable series order and use 'none'/'reverse'.

Normalization, source-safe tooltips/labels/CSV, analytical component values, SVG/image output, SSR and save/restore retain their existing contracts. Ordering does not change normalization denominators. For stack-preserving stable IDs, charts and Pivot use the same domain helper. Public policy types are ChartStackOrder (Charts root/core) and PivotChartStackOrder (Pivot root/charts facade).

Pivot Customize → Chart appearance exposes Stack order from the shared option schema. Axes and scales → series → Stack order sets a member policy; Automatic removes that override. Supported combination column/area/line switches retain a policy, while unsupported layer switches clear it. Native bar and line/area series also expose the policy. Localize with localeText.chartOptions.labels.stackOrder, descriptions.stackOrder, selectValues.none, reverse, ascending, descending, appearance, and existing automatic/axisField/reset hooks. UI policy keys are chartOptions.stackOrder and axes.seriesStackOrder, plus existing parents. Resetting only the global option does not remove member overrides.

Set seriesOverrides[id].interpolation to choose the shape between retained observations. It supports native line, smoothLine, steppedLine, area, steppedArea, stackedArea and normalizedArea, plus explicitly assigned line and area layers in columnLine/areaLine. Other families and combination column/scatter/range/helper layers reject it. There is no chart-wide chartOptions.interpolation alias.

ValueShape
'linear'Straight connections.
'smooth'Existing monotone-X smoothing algorithm, retained for compatibility.
'step'Transition at the midpoint between adjacent X coordinates.
'stepBefore'Vertical transition at the previous observation, then horizontal connection.
'stepAfter'Horizontal connection to the next observation, then vertical transition.
'monotoneX'D3 monotone cubic interpolation along screen X.
'monotoneY'D3 monotone cubic interpolation along screen Y.
'natural'Natural cubic spline.
'catmullRom'Centripetal Catmull–Rom spline (alpha 0.5).
'bumpX'Cubic connections with horizontal tangents.
'bumpY'Cubic connections with vertical tangents.
seriesOverrides: {
revenue: { interpolation: 'natural' },
target: { interpolation: 'stepAfter' },
},

Omitting the property keeps the chart-type default: smoothLine uses 'smooth', steppedLine/steppedArea use 'step', and other supported native types use 'linear'. Combination paths default to straight connections. Removing an override restores that default. ChartInterpolation is exported from Charts root/core; PivotChartInterpolation is exported from Pivot root/charts.

Curves operate on projected screen coordinates after axis transforms, ordering, stacking and sampling. For numeric/time X, observations follow the existing ordered-X contract. X/Y names describe physical coordinates; axis reversal does not rename the curve. Use monotone X with monotonic screen X, and monotone Y with monotonic screen Y. Natural and Catmull–Rom curves can overshoot observations or cross neighboring stack boundaries; interpolation changes visual geometry, not source values, normalization totals or analytical component values. Curved areas interpolate both top and baseline; directional steps reverse their return-edge policy so both boundaries transition at the same physical X. Stacked sign changes and null/configured calendar gaps retain their existing run boundaries; curves leave missing observations disconnected by default; the per-series connectNulls policy can join them. Single-observation runs keep their source marker. No additional measured points are created.

Existing viewport clipping, dense path budgets and envelope sampling still apply. Curves connect retained observations; sampling is not a guarantee of reproducing every bend in the unsampled spline. Canonical IDs, source mark activation, tooltip values, legend, CSV, saved models and SVG/SSR retain their existing contracts. Navigator previews use the same native path owner and inherited per-series policies where that series is projected as a line/area.

Pivot Customize → Axes and scales → series → Interpolation selects the same policy. Automatic removes the override. Combination line/area switches retain it; column/scatter/range/helper switches clear it, and gallery transitions to unsupported chart types remove it. UI-policy key: axes.seriesInterpolation. Localize with localeText.chartOptions.interpolation, axisField, automatic and selectValues entries for every curve above.

The named curve choices follow the MUI line interpolation reference and D3 curve definitions. Legacy 'smooth' retains its original algorithm rather than aliasing it to a new curve.

seriesOverrides[id].connectNulls is an optional boolean, default false. Set it to true to connect finite observations across intervening null/missing values. It supports native line, smoothLine, steppedLine, area, steppedArea, stackedArea and normalizedArea, and explicitly assigned combination line/area layers in columnLine/areaLine. Other families and column/scatter/range/helper layers reject the option. There is no chartOptions.connectNulls alias.

seriesOverrides: {
revenue: { interpolation: 'natural', connectNulls: true },
target: { connectNulls: false },
},

The selected curve joins existing finite endpoints; it does not fill the data matrix, create values or source marks, or extend before the first finite observation or after the last one. Tooltips, hover/selection targets, analytical values, CSV and raw activation payloads still refer only to source observations. Leading/trailing/all-null runs remain empty. Invalid nonfinite values are not treated as joinable nulls; the normal data boundary still rejects them.

For time axes, axes.x.missingPeriods: 'gap' remains authoritative. A present category with a null value is joinable, but an absent configured calendar period still splits the path. To join both, explicitly set the time axis to missingPeriods: 'connect' as well. This applies to the assigned named X axis. The calendar policy uses the shared dates package.

Zooming into a long null run retains the nearest finite bracketing observations as clipped decorative geometry, including when no source mark is visible in the window. It does not extrapolate outside the accepted dataset or fetch unavailable remote observations. Connection occurs before path sampling; existing budgets and extrema/envelope policies still apply. Stacking/normalization uses the original finite endpoint values and baselines. Combination stacked areas still split at changes between positive and negative contributions; connectNulls does not join unrelated sign baselines. Navigator previews inherit the per-series policy through the same path owner; navigation.navigator.seriesOverrides[id].connectNulls can explicitly override it for a supported line/area overview type. The option survives saved models and SVG/SSR rendering through ChartSeriesOverrides/PivotChartSeriesOverrides.

Pivot Customize → Axes and scales → series → Connect missing values offers Automatic (remove the property and use false), Connect (true) and Leave gaps (false). Explicit false is retained in the saved model. Switching between supported combination line/area layers retains the setting; switching to an unsupported layer or chart type clears it. UI-policy key: axes.seriesConnectNulls. Localize via localeText.chartOptions.connectNulls, axisField, automatic, and selectValues.connected/gapped.

See the MUI missing-point connection reference for the upstream series option. Explicit calendar-gap precedence follows the existing Charts time-axis contract.

seriesOverrides[id].baseline accepts a finite number, 'min' or 'max'. It is available on ungrouped native area/steppedArea series and explicitly assigned, unstacked combination area layers in columnLine/areaLine. Omitting it keeps the zero baseline, clipped to the current domain when needed. There is no chart-wide chartOptions.baseline alias.

seriesOverrides: {
revenue: { baseline: 'max' },
costs: { baseline: -25 },
},

A numeric baseline contributes to the automatic Y domain for its visible source series and assigned Y axis. Hidden series and unknown/dormant IDs do not contribute. Existing automatic scale rules, including zero-inclusive linear domains, remain in effect. 'min'/'max' resolve the final numerical axis domain, including authored bounds, analytical extensions, runtime domains and shared bounds across X-axis partitions; they do not mean the series’ observed minimum/maximum or a fixed physical screen edge. Reversing Y changes their physical position. Numeric baselines remain fixed raw values. Authored/runtime bounds clip the fill rather than changing that authored value or forcing the domain to expand.

The setting changes the fill boundary, not source line endpoints or values. It does not offset the data, create observations, or change raw tooltips, mark activation, CSV, normalization denominators or analytical component values. Curves interpolate the existing upper and baseline edges; null-connection, calendar-gap, sampling and viewport policies still apply. An all-null series has no fill even when an authored numeric baseline participates in its axis domain. SVG/SSR and saved models retain the setting. Public policy types are ChartAreaBaseline (Charts root/core) and PivotChartAreaBaseline (Pivot root/charts).

Authored baselines are rejected on stacked/normalized native areas, native area/stepped-area series with a stackGroup, grouped combination areas, line/column/scatter/range/helper layers and unrelated chart types, even when the authored number is zero. Stacks keep their shared cumulative baselines. Native logarithmic areas accept positive numeric baselines and 'min'/'max'; zero/negative numeric values reject atomically. The omitted legacy baseline still uses the logarithmic domain floor. Combination-area logarithmic restrictions remain in effect.

Area/stepped-area navigator previews inherit the setting. Override with navigation.navigator.seriesOverrides[id].baseline for those overview types. A line-only preview ignores an inherited area baseline; explicitly authoring a baseline for a line overview rejects as unsupported.

Pivot Customize → Axes and scales → series → Area baseline offers Automatic, Axis minimum, Axis maximum and Fixed value. Fixed value exposes Baseline value. Automatic removes the override; clearing the numeric input does the same. An explicit zero is retained on linear axes. Switching to another unstacked area retains the setting; switching to an unsupported layer/type clears it. Entering a stack group clears the baseline atomically and hides its controls. UI-policy key axes.seriesBaseline controls both fields. Localize with localeText.chartOptions.baseline, baselineValue, axisField, automatic and selectValues.baselineMin/baselineMax/baselineNumber.

See MUI area baselines for the upstream fill policies and stacked-area restriction.

seriesOverrides[id].markers accepts true, false, 'start' or 'end' for native line, smoothLine, steppedLine, area, steppedArea, stackedArea and normalizedArea, and explicitly assigned combination line/area layers. Omitted main-chart settings keep all retained point glyphs; true does the same and false hides them. Endpoint strings select one persistent source glyph per series; enabled transient inspection can visually substitute it until leave, blur or Escape. There is no 'all' string or chartOptions.markers alias. Unsupported families/layers reject endpoint modes.

seriesOverrides: {
revenue: { markers: 'end', marker: 'star', markerSize: 6 },
target: { markers: false },
},

Start/end mean the first/last finite observation in the assigned X ordering of the accepted frame, before viewport clipping, physical axis reversal and path sampling. Numeric/time X uses its ordered coordinates; categories use canonical category order. Nulls are skipped without fabricating values. A series with no finite observations has no endpoint. These modes do not mean the first/last currently visible point, and axis reversal does not exchange them. A source endpoint outside the visible X/Y domain has no persistent glyph; zooming does not substitute an interior point. Remote partial frames can select only their accepted observations.

Dense paths keep their current sampling budgets. An eligible endpoint omitted by path sampling can be retained as one extra source glyph when the series has a nonzero path budget; it does not create another path segment or synthetic data. Existing path/series omission limits remain. Data labels are independent of marker visibility: presentation.dataLabels can still label retained observations with hidden or endpoint-only glyphs, subject to the existing label-density rules. markerSize retains its pixel-radius setting and now also applies to supported combination path glyphs. Raw activation values, CSV, analytical values, stacks, IDs, save/restore and SVG/SSR remain source based.

seriesOverrides[id].marker now supports 'circle' | 'square' | 'diamond' | 'triangle' | 'cross' | 'star' | 'wye' wherever the chart’s marker-shape capability is available. The four existing shapes retain their geometry. Cross/star/wye use shared unit SVG symbols scaled around the source position, with nominal area equal to a circle of the supplied radius. Shape changes do not change mark identity or click/keyboard payloads. Source observations retain keyboard activation even when glyphs are not drawn; the shared source traversal uses at most one temporary focus target instead of hidden per-datum DOM. Existing chart navigation/crosshair controls remain independent. Persistent endpoint selection remains separate from the runtime predicate below and does not implement transient pointer-highlight substitution.

Public types ChartMarkerMode and ChartMarkerShape are exported from Charts root/core, with PivotChartMarkerMode and PivotChartMarkerShape on Pivot root/charts. Native navigator previews hide default glyphs as before; explicit/inherited per-series modes and shapes apply through the same path owner, and navigation.navigator.seriesOverrides[id] can override them for supported overview types. The accepted overview frame determines its own endpoint.

Pivot Customize → Axes and scales → series → Markers offers Automatic, All retained points, Hidden, First observation and Last observation. Automatic removes the override; all/hidden save boolean true/false. The shared UI authors the effective combination path type when needed. Supported line/area switches retain modes; switching to an unsupported layer/type clears them. UI-policy key axes.seriesMarkers controls this selector. Localize with localeText.chartOptions.markersMode, axisField, automatic and selectValues.markersAll/markersNone/markersStart/markersEnd.

Marker shape remains in the existing Customize → Colors → series → marker control and its paginated series editor, using colors.marker. Localize new captions with localeText.colors.cross, star and wye; existing shape captions and colors.marker label function remain. Changing marker visibility, shape, interpolation, baseline or null connection preserves sibling path settings.

See MUI line marks for the upstream shape and endpoint choices. The source-frame endpoint and sampling contracts above describe Charts/Pivot behavior.

rendererOptions.markers.isVisible(context) is an optional synchronous boolean predicate for native line/area glyphs and explicit combination line/area layers. Use it to select source observations independently of the sampled line vertices. It refines the persisted marker mode: markers: false remains hidden; start/end can only keep or suppress their accepted-frame endpoint. It does not filter paths, data, analytical components or CSV, and does not create values for nulls or decorative boundary points.

const chart = await createChart({
container,
chartType: 'line',
dataset,
seriesOverrides: { revenue: { markers: true } },
rendererOptions: {
markers: {
isVisible: context =>
context.renderMode === 'preview' ||
(context.series.id === 'revenue' && context.categoryIndex % 100 === 0),
},
},
});

ChartMarkerContext contains chartId, the current rendered chartType, renderMode: 'chart' | 'preview', canonical accepted-frame categoryIndex/seriesIndex, readonly category/series descriptor snapshots, finite value, assigned xAxisId/yAxisId, and SVG plot coordinates point: { x, y }. Main-chart value remains the raw accepted-frame value through hidden-series projection, named axes, stacking, normalization and overflow scaling; it is not the cumulative or percentage endpoint. Indices refer to the canonical frame before series projection, not the glyph’s DOM index or X-sort position. Descriptor/path/label metadata and the context are frozen snapshots; treat them as readonly.

Preview values, descriptors and indices belong to the accepted preview projection, which can contain normalized or derived metrics. Preview chartType describes that native overview. Inspect renderMode rather than assuming a preview value equals the original main-frame value. Previews keep their normal default hidden glyphs; explicitly inherited or navigator-overridden marker modes use the same predicate. Branch on renderMode to apply a different preview policy.

The callback examines eligible visible finite source candidates for regular glyphs before bounded marker sampling. Transient source highlights bypass this predicate without invoking it again. It can select a source observation absent from the line’s sampled vertices, such as every 100th point. Matching glyphs obey the existing per-series/chart marker budgets unless chartOptions.sampling: 'none' disables that reduction; when matches exceed the budget, bounded source-order extrema sampling applies. Glyph/focus order follows source X order. Hidden series, nulls, out-of-domain points, zero-budget series and markers: false do not invoke it. Native line/area families and explicit combination line/area layers support it; it does not customize scatter, column, range, flow or matrix glyphs. Existing path labels are not suppressed by the predicate; newly selected source glyphs can also participate in existing data-label density rules.

Keep the predicate pure and synchronous. It can run again for data/style changes, resize, navigation, exports, SSR and hydration; invocation counts are not a business-event contract. A nonboolean result or invalid predicate type reports invalid-marker-result through rendererOptions.onDiagnostic and retains the built-in visibility locally. Reports are bounded to once per root SVG lifecycle, including preview/repaint calls. Observer errors are isolated, and missing observers use the existing console diagnostic behavior. Predicate exceptions propagate like other renderer formatter exceptions.

These callbacks are runtime-only. Saved models contain no predicate. Re-supply it after restore, to renderChartToSVG, and to hydrateChart for matching output and future repaints. Hydration can reconcile a different client predicate while retaining the root SVG. Public runtime types are ChartMarkerContext, ChartMarkerPredicate and ChartMarkersRendererOptions, exported from Charts root/core. ChartsRendererOptions.markers carries the options.

For Pivot, configure the same options with createPivotChartsRenderer({ markers: { isVisible } }) and install that renderer in the chart plugin/standalone mount configuration. Public aliases PivotChartMarkerContext, PivotChartMarkerPredicate and PivotChartMarkersRendererOptions are available from Pivot root/charts. Pivot’s persisted Markers selector still narrows the host predicate; Automatic/all does not disable application filtering. No function editor or serialized callback field is added to the inspector.

This covers source predicates corresponding to MUI callback-based mark selection. Transient source highlighting is implemented independently of the regular marker predicate; see the transient source-point highlights section.

Use chartOptions.sampling on line, smoothLine, steppedLine, area, steppedArea, stackedArea, normalizedArea, columnLine, and areaLine. Pivot exposes the same Path sampling selector under Customize → Appearance.

await chart.update({ chartOptions: { sampling: 'lttb' } });
ValueBehavior
'auto' (default)Preserves existing defaults: M4 reduction for ordinary paths and combination envelopes; native stacked and normalized areas retain all accepted observations.
'none'Retains every accepted path observation and eligible marker, without path or marker downsampling. Normal dataset limits still apply. Large accepted datasets can cost more time and DOM nodes.
'm4'Keeps first, minimum, maximum, and last observations in each X bucket, in source order.
'minmax'Keeps overall endpoints and each interior X bucket’s minimum and maximum observations, in source order.
'lttb'Largest triangle three buckets: keeps endpoints and chooses observations that approximate the path’s shape. It does not guarantee retention of every peak.

Explicit algorithms use the existing width-based path budget (up to 1,200 observations per series, with a 4,800 observation chart allocation). Separate finite runs share that budget; very small budgets can omit entire runs. Runs are split before reduction, so omitted runs never create a connecting segment. Viewport boundary neighbors can add decorative vertices outside the visible window. Sampling operates within the current viewport; retained observations can change after zooming or resizing. Preview mode may already project a reduced dataset before the renderer runs, including when sampling is 'none'.

Explicit sampling of stacked line/area paths uses one bounded set of source indices across cumulative edges in each X axis, Y axis, and stack group partition. Every member applies that selection to its original finite/sign runs. Range bands reduce their lower and upper edges together. These are display approximations; LTTB favors shape, while M4 and min/max retain extrema in their chosen buckets. Null handling, connectNulls, calendar gaps, stack order, and normalization still apply. Column and scatter layers in combination charts keep their existing rendering policy; this option does not enable bar envelopes or scatter sampling choices. Specialized chart types do not accept this option.

Raw datasets, automatic domains, CSV values, source indices, and interaction values retain their original meaning. A runtime marker predicate is evaluated on eligible source observations before its independent M4 marker budget; its result need not match the path’s selected vertices. 'none' also disables that marker reduction. Boolean and endpoint marker modes remain authoritative.

The choice is serializable in chart.model, survives save/restore, and applies to SVG server rendering and hydration. ChartSampling and PivotChartSampling name the public five-value union. Resetting the appearance options restores 'auto'. For a narrow update, preserve other authored chartOptions fields if needed by your application’s update flow.

seriesOverrides[id].strictStepCurve accepts a boolean (default false) on native line, smoothLine, steppedLine, area, steppedArea, stackedArea, and normalizedArea, and explicitly assigned combination line/area layers. It takes effect with interpolation: 'step' | 'stepBefore' | 'stepAfter' when the assigned X layout has categorical bands.

seriesOverrides: {
forecast: { type: 'line', interpolation: 'stepAfter', strictStepCurve: true },
}
ValueStepped path edges on categorical bands
Omitted or falseExpand each finite run horizontally by half one category cell at each edge.
trueStart and finish at the exact observation positions.

Set true to retain the earlier exact-endpoint shape on categorical combinations. Combination charts default to band-centered categorical X positions. Native line/area charts default to points; axes.x.categoryPlacement can choose bands or points explicitly. Continuous numeric/time axes have no category band width. strictStepCurve itself does not change X spacing. Other interpolation methods ignore the flag. Unsupported families and column/scatter/range layers reject it; nonboolean values reject before a chart update replaces the frame.

Expansion uses the category cell width, including after viewport changes and reversal. It never infers cell width from the distance between sampled points. Each null/sign-delimited retained run expands independently; a singleton can span its own cell. connectNulls: true can join null-separated observations before interpolation, while explicit calendar gaps retain their existing rule. Sampling can omit source observations/runs; expansion only decorates the retained run edges and does not restore omitted data. Area top and baseline edges expand together and return with the matching directional step. Plot clipping still applies to authored X/Y domains, including positive logarithmic line axes.

Expanded endpoints are decorative geometry. They do not add data, glyphs, predicate calls, focus targets, labels, domain values, CSV rows, or activation values. The boolean persists in models, SVG/SSR and hydration through ChartSeriesOverrides/PivotChartSeriesOverrides. Navigator overrides can carry it for supported overview line/area types. Point overviews retain exact geometry; explicit band placement is inherited by their selected X axis.

Pivot exposes Customize → Axes → [series] Step endpoints with Automatic, Expanded bands, and Strict points. Automatic removes the authored flag and restores the default. Editing an implicit combination path authors its effective line/area type; switching that layer to a nonpath type clears the flag. Reset appearance clears it with other series overrides. The controls use shared localized captions (strictStepCurve, expandedSteps, strictSteps).

axes.x.categoryPlacement and axes.xAxes[id].categoryPlacement accept 'point' or 'band' on native line, smoothLine, steppedLine, area, steppedArea, stackedArea, normalizedArea, columnLine, and areaLine. ChartCategoryPlacement and PivotChartCategoryPlacement name the public union. The catalog advertises support through categoryAxisPlacement.

axes: {
x: { scale: 'category', categoryPlacement: 'band' },
xAxes: { detail: { categoryPlacement: 'point', reverse: true } },
}
SettingObservation placement
Omitted / AutomaticNative line/area charts use points; combinations use bands, preserving existing defaults.
'point'The first and last observations occupy the ends of the projected X range; a singleton sits in the center.
'band'Observations sit in the centers of equal category cells, with half a cell at each outer edge. A singleton owns the full cell.

This is a placement policy for scale: 'category', including when that scale is implicit. Other chart families and time/numeric X axes reject the field. There is no 'auto' string: remove the field to restore the chart family default. Direct API updates reject invalid settings before replacing a frame.

Band placement enables expanded native stepped lines/areas. With step, stepBefore, or stepAfter, run edges extend to cell edges by default; seriesOverrides[id].strictStepCurve: true keeps exact observation endpoints. Point placement does not expand steps. Nonstep interpolation still uses the chosen observation positions. Sampling uses the same positions; it does not create larger cells from the distance between retained observations. Stacks keep their current Y geometry and source-index alignment.

Point combinations with columns reserve half a column-group width at the outer edges so columns remain inside the plot; their observation range is inset by that margin. Band combinations keep the existing widths and centers. Labels and category-group bands follow the registered projection, including reversal and independent named axes. Placement does not reorder categories or change raw values, domain values, CSV, focus targets, tooltip identities, or activation values. The observed X coordinates and decorative geometry change.

Category viewport/brush ranges continue to use stable category IDs. Changing placement retains the semantic selection and projects it onto the new cells; zoom, nearest-category lookup, references, shapes, crosshairs and compatible peers use the same axis projection. Compact navigators inherit their selected X axis’s placement. Models, SVG/SSR and hydration retain explicit settings.

Pivot exposes Customize → Axes → [axis] Category placement with Automatic, Point positions, and Centered bands, separately for the primary and named X axes. Numeric/time scale changes clear the category-only setting; gallery changes clear it when unsupported. Other axis styles, category mappings/groups and compatible semantic windows are preserved. Reset appearance clears authored axes, restoring family defaults. Localized captions are categoryPlacement, categoryPoint, and categoryBand under chartOptions.

Native line/area charts and explicitly assigned combination line/area layers show a transient source-point glyph during pointer or existing datum-focus inspection. This visual is independent of regular markers and rendererOptions.markers.isVisible: hidden, predicate-rejected and sampled-out source observations can still be highlighted without rerunning that predicate.

OptionDefaultEffect
chartOptions.disableLineItemHighlightfalseSuppresses transient line/area glyphs chart-wide.
seriesOverrides[id].disableHighlightOmitted / falseSuppresses transient glyphs for that series when true.

Both are persisted booleans supported by line, smoothLine, steppedLine, area, steppedArea, stackedArea, normalizedArea, columnLine, and areaLine. Per-series flags require explicit type: 'line' or 'area' in combinations; unsupported families/layers and nonboolean values reject before replacing a frame. The global flag wins over a per-series false. Disabling this visual leaves ordinary markers, source tooltips, crosshairs, selection and peer highlighting enabled under their respective settings.

chartOptions: { disableLineItemHighlight: false },
seriesOverrides: {
revenue: { markers: 'end', disableHighlight: false },
}

Hovering an existing source glyph inspects its exact series/category. Hovering a line/area path chooses the nearest ordered X observation in that source series; plot-background inspection uses navigation.xAxis (primary by default) and its path series. Shared tooltip mode can highlight the same category across the assigned X axis. Numeric/time order, category bands, reversal, named axes and stacked/normalized Y projections use the actual registered geometry. A null or clipped observation has no glyph; no interpolated point or unloaded remote row is invented. This is nearest-source inspection, not continuous curve-distance hit detection.

Transient glyphs use the shared marker shape and raw-value color, with radius max(4, markerSize/defaultRadius) + 2. Runtime element styles/formatters apply as kind: 'mark' with canonical raw values and source indices. The decorative SVG nodes have data-pivot-chart-transient-marker, aria-hidden="true" and pointer-events="none"; they are not extra datum/roving/activation targets. They add no data labels, source rows, predicate calls, path vertices, or CSV rows.

For saved 'start'/'end' marker modes, an enabled active glyph visually replaces the persistent endpoint until inspection leaves, blurs, is cancelled or Escape is pressed. Endpoint paint is suppressed while its existing pointer/focus target remains active; original styles are restored. Retained datum focus can keep its focus glyph after the pointer leaves until blur or Escape. Repaints discard pointer state and recompute any restored datum-focus highlight from the new frame. Destruction discards the state; drag/brush pointer movement suppresses it. Source keyboard traversal can inspect hidden/sampled-out eligible observations using the separate focus target described below. Decorative path highlights do not convert pointer clicks into datum activation.

Glyph feedback is immediate, including with rendererOptions.tooltip: false. Source-point tooltip inspection uses existing delays and canonical raw values, even when the visual is disabled. Preview charts do not show transient glyphs. Models retain only the authored boolean policies; SSR starts without transient state. Image/SVG/PDF snapshots omit transient glyphs and restore persistent endpoints in the exported clone without changing live hover/focus state.

Pivot exposes Customize → Chart appearance → Disable point highlights and Customize → Axes → [series] Point highlights (Automatic, Enabled, Disabled). Automatic removes the per-series flag. Layer/gallery changes clear it when unsupported; appearance reset restores default policies. Shared public ChartSeriesOverrides/PivotChartSeriesOverrides carry disableHighlight, and ChartOptionKey includes disableLineItemHighlight. Localized captions are the schema label/description plus disableHighlight, highlightEnabled and highlightDisabled under chartOptions.

boxPlot, violin and statistical rangeInterval support axes.xAxes and seriesOverrides[id].xAxis in standalone Charts and Pivot. Primary X remains the default. Each declared X axis owns its category/time/numeric projection, reversal, labels, grouping and window; unassigned declared axes can still display their captions. The existing limit is 32 named axes. Unknown assignment IDs are rejected before the model changes.

axes: {
x: { scale: 'category', level: 0 },
xAxes: {
elapsed: {
scale: 'log', level: 1, reverse: true,
position: 'top', title: 'Elapsed', unit: 's',
tickValues: [1, 10, 100],
},
},
ySecondary: { title: 'Rate' },
},
seriesOverrides: { rate: { xAxis: 'elapsed', yAxis: 'secondary' } },
viewport: { xAxes: { elapsed: { scale: 'log', min: 1, max: 100 } } },
navigation: { enabled: true, xAxis: 'elapsed', navigator: true },

Primary and named X axes accept level (a category-path index, 0–127) or data (a map from canonical category ID to an X value). An explicit data entry takes precedence over level; missing entries fall back to the selected path level, or the last path element by default. These mappings change projection/captions without rewriting category identities, matrix cells or distribution records. Time values follow the shared dates package; numeric values must be finite and logarithmic keys positive. data accepts at most 100,000 entries. Models copy and persist these definitions; runtime providers and callbacks remain separate.

All named X axes accept category, time, linear, log, symlog and signed sqrt. They reuse the primary X controls described above: numeric bounds, units/formatting/tick controls, positive symlog constant, reversal, time calendar intervals/missing-period policy, and categorical group bands. Default categories retain band spacing. Explicit categoryPlacement remains limited to supported line/area/combination families. Vertical statistical charts place named X axes at top/bottom/none (default top); horizontal box plots place them at left/right/none (default right). Their primary physical aliases remain unchanged. Measured gutters reserve space per displayed axis, including titles and grouped bands. Pivot offers named-axis creation/removal, path-level selection, scale/format controls and per-series assignments; orientation/type changes clear incompatible positions while retaining valid settings.

Each X frame contains only its assigned distributions. Box/violin slots are grouped per series within that frame. Statistical intervals aggregate only measures sharing both X and Y assignments, so observations with different X units never form one interval. Value-axis domains still include all accepted observations assigned to that Y axis across X frames, independently of visible X windows. Hidden/unbound series remain excluded. Generic value references render once; category references use xAxis, shapes use xAxis and yAxis, and semantic notes follow the source measure’s actual X/Y projection.

Use viewport.xAxes[id] for an independent window, navigation.xAxis for navigator/zoom/pan targets and rangeSelection.xAxis for a brush target. Synchronization, peer hover and continuous remote-detail fullDomain.xAxes metadata use the same stable IDs and raw mapped coordinates. Runtime/source/sync Y hints continue to describe primary Y only and exclude secondary/named-Y observations. Secondary/named Y bounds can be authored separately. These X windows do not add independent value-axis navigation. Save/restore, server SVG, source activation and raw CSV retain the existing source-data contracts; histogram collections have their own bound-measure contracts; statistical analytical overlays remain separate.

candlestick, ohlc and volumePrice accept primary axes.x.scale: 'linear' | 'log' | 'symlog' | 'sqrt' in standalone Charts and Pivot. The default remains category, and existing time axes remain available. Numeric X uses the mapped category’s actual numeric key, not the row index or an OHLC/volume measure. Values must be finite; log keys must be positive. Signed symlog/sqrt keys remain supported.

axes: {
x: {
scale: 'log', level: 0, min: 1, max: 1000,
reverse: true, position: 'bottom', title: 'Observation distance', unit: 's',
tickValues: [1, 10, 1000],
tickFormat: { notation: 'standard', maximumFractionDigits: 2 },
},
},
viewport: { x: { scale: 'log', min: 10, max: 1000 } },
navigation: { enabled: true, navigator: true },

Primary financial X also accepts level (category-path index 0–127) and data (up to 100,000 canonical-category-ID/value entries). Explicit map entries take precedence over the selected path level; missing entries fall back to that level or the last path element. Category maps affect captions; numeric/time maps provide coordinates. Canonical category IDs, financial fields and source/export values remain unchanged. Date/time values keep the shared dates-package contract.

Numeric X supports positive symlog constant (default 1), min/max, reverse, unit, tickFormat.notation/minimumFractionDigits/maximumFractionDigits, tickValues, tickCount, tickMinStep, tickMaxStep, tickSpacing, top/bottom/none placement, title, lineColor, labelColor and titleColor. Nonlinear ticks and pointer inversion use the same raw coordinate transform. Explicit category point/band placement remains limited to the chart families advertising that capability.

Candle/OHLC centers and line/step-line price paths follow real numeric spacing. Glyph widths are bounded by the nearest distinct visible numeric X gap; numeric volume bars can have fractional widths below 0.1 pixels instead of overlapping close observations. Price and volume panes share X centers, reversal, bounds and viewport windows. Their Y scales, OHLC close activation and volume activation retain existing ownership. Numeric windows are increasing raw min/max values even when reversed. Numeric X has no calendar missing-period interpolation policy.

Navigation, navigator, brushes, category references, shapes, notes, synchronization and continuous data-source fullDomain.x use those same raw coordinates. Remote full-domain Y remains price-only; fixed volume bounds are authored in axes.ySecondary. Save/restore and server SVG preserve the configuration, and CSV retains canonical observations with the existing current/all export policy. Pivot exposes numeric scale/format/tick controls and primary path-level selection; edits and gallery transitions preserve valid primary mappings. Candlestick/OHLC collections are described below. Paired volume-price collections, named X and pane sizing are described below; additional pane/Y axes and volume Y synchronization remain separate capabilities. Invalid direct axis updates are rejected atomically.

candlestick and ohlc support multiple bound price groups in standalone Charts and Pivot. Parallel bindings.open, high, low and close arrays identify each group by stable source-series IDs. Each group needs four distinct fields. A helper channel (open, high, low) can contain one shared field or the common group count; close must contain one distinct owner per group. Missing fields, inconsistent counts and duplicate owners are rejected. Numeric values are never inferred from column position. The catalog no longer caps these two types at four selected source fields; normal chart limits still apply.

bindings: {
open: ['openA', 'openB'], high: ['highA', 'highB'],
low: ['lowA', 'lowB'], close: ['closeA', 'closeB'],
},
axes: {
x: { scale: 'time', level: 0 },
xAxes: { elapsed: { scale: 'linear', level: 1, title: 'Elapsed', unit: 's' } },
y: { title: 'Market A' },
yAxes: { marketB: { scale: 'log', title: 'Market B', position: 'right' } },
},
seriesOverrides: { closeB: { xAxis: 'elapsed', yAxis: 'marketB' } },
navigation: { enabled: true, xAxis: 'elapsed', navigator: true },

The close field owns its group’s axes. seriesOverrides[closeId].xAxis selects primary or a declared axes.xAxes entry. yAxis selects primary (axes.y), secondary (axes.ySecondary, explicit or default), or a declared axes.yAxes entry. Unset assignments default to primary. Open/high/low helpers inherit the owner assignment; an explicitly conflicting helper assignment is rejected. A broadcast helper can participate in several projections, so an explicit assignment must agree with every owning group. Unbound measures cannot acquire non-primary financial axes.

Every X collection supports the existing category/time/four numeric scales, data maps, path level, bounds/formatting/ticks appropriate to its scale, reversal, grouped category captions, styles and top/bottom/none placement. Value collections support all four numeric scales, bounds/reversal, units/formatting/ticks, styles and left/right/none placement. Named axes retain the existing maximum of 32 entries per collection. Unknown IDs and invalid scale/domain changes fail before the live model changes. Declared unused axes can retain decorations and references without inventing source marks.

Groups assigned to the same X frame occupy stable neighboring slots, including when they use different Y axes. Slots are based on bound groups before window clipping or missing-row omission. Continuous collections reserve an edge margin, and widths/offsets remain bounded by the nearest distinct continuous-observation gap, including irregular times. One-group geometry keeps its existing defaults. Price paths and all six priceStyle values share these assignments; candleFill and the existing rising/falling palette retain their behavior. This does not enable arbitrary per-group colors, visibility or a new financial legend.

Each Y domain uses complete OHLC observations assigned to it across all X frames. A missing price field omits only that group’s observation at that category. Existing high/low normalization and raw close activation remain unchanged. A log Y axis validates only its assigned complete groups. Runtime/source/synchronized Y metadata describes primary price groups only; secondary/named prices compute from their own groups or authored bounds. Secondary/named Y synchronization remains separate.

viewport.xAxes[id], navigation.xAxis and rangeSelection.xAxis select independent windows and interaction targets. The navigator defaults to the first eligible close field on its target X; explicitly selected series or all retain the existing raw-field overview policy. Generic Y references render once, category references use xAxis, and shapes select xAxis/yAxis. Source notes on close fields follow the group’s actual slot and value projection, including when that close field also helps another group. Broadcast helpers can have several projected positions; use a close owner for an unambiguous source note or an explicit X/Y shape for a specific coordinate.

Current CSV windowing follows each group’s owner for all four fields; shared helpers use the union of their owners’ windows. dataScope: 'all' retains canonical loaded matrix cells. Saved bindings/axis assignments, server SVG, source activation and remote-detail/full-domain IDs preserve the same raw source contracts. Pivot preserves explicitly valid grouped bindings instead of reducing them to singleton semantic inference; singleton inference stays available. Author grouped role bindings through the model/API, then use the shared Pivot X/Y forms and close-owner selectors. Gallery transitions clear incompatible helper assignments.

volumePrice uses the paired collection contract described below: fixed price/volume Y roles with close-owned X frames and optional pane ratio. It does not acquire candlestick/OHLC named Y axes.

volumePrice accepts parallel bindings.open, high, low, close and volume arrays for multiple price/volume pairs. The OHLC helper broadcast rules match candlestick/OHLC collections, but every pair needs one distinct close owner and one distinct volume field. Volume fields cannot also be price fields in any group. Inconsistent counts, duplicate close/volume owners and conflicting field roles are rejected before the model changes. The former five-series maximum is removed; normal chart limits still apply.

bindings: {
open: ['openA', 'openB'], high: ['highA', 'highB'],
low: ['lowA', 'lowB'], close: ['closeA', 'closeB'],
volume: ['volumeA', 'volumeB'],
},
axes: {
x: { scale: 'time', level: 0 },
xAxes: { elapsed: { scale: 'linear', level: 1, title: 'Elapsed' } },
y: { scale: 'log', title: 'Price' },
ySecondary: { scale: 'sqrt', title: 'Volume' },
},
seriesOverrides: { closeB: { xAxis: 'elapsed' } },
chartOptions: { volumePaneRatio: 0.4 },
navigation: { enabled: true, xAxis: 'elapsed', navigator: true },

A close field owns X for all four price fields and its paired volume. Use primary or a declared axes.xAxes ID, with the existing category/time/four numeric scales, mappings, reversal, formatting/ticks/styles and independent viewport.xAxes[id]. Helpers and volumes inherit X; conflicting explicit assignments and non-primary assignments on unbound fields are rejected. Each pair occupies the same slot in both panes, including pairs sharing an X frame. Continuous slots and bar widths use real observation spacing. Missing volume can omit a volume bar while retaining a complete price observation; an incomplete OHLC group exposes neither a price observation nor its paired volume at that category.

Y roles remain fixed: every price field uses primary axes.y; every bound volume uses secondary axes.ySecondary. Matching explicit Y roles are accepted, while swaps and named Y axes are rejected. Price and volume each share a scale across all accepted groups before X-window filtering. The existing scale/bounds/reversal/unit/format/tick/physical-axis controls remain independent per pane. Runtime/source/synchronized primary Y metadata covers prices only; secondary volume bounds are authored separately, and independent volume Y synchronization is not added.

chartOptions.volumePaneRatio is volume-price-only, accepts a number from 0.1 through 0.8, and sets volume’s fraction of usable height after the fixed pane gap. The generic Pivot control uses step 0.01 and displays default 0.24. When the option is absent or reset, the renderer preserves automatic 24% sizing with the legacy small-pane minimum policy. An authored ratio, including 0.24, requests that exact fraction. Price receives the remaining usable height. The Pivot control ID is chartOptions.volumePaneRatio; its label and help use localeText.chartOptions.labels.volumePaneRatio and localeText.chartOptions.descriptions.volumePaneRatio. This option persists in models and server SVG; it does not create extra panes or change the gap policy.

Generic value references render once in their pane; category references target xAxis and render in both panes. Shapes, close/volume source notes, hover/crosshair and canonical activation use the pair’s actual X slot and owning Y projection. Volume hover publishes the paired close owner’s raw X coordinate and stable axis ID. Current CSV follows X ownership for all five fields; shared OHLC helpers use the union of their owners’ windows, while all-scope CSV retains canonical loaded values. Separate close/helper/volume-only overview datasets retain their authored raw values and use source bindings for X eligibility.

Pivot preserves valid explicit paired bindings, exposes X selectors on close owners, fixed price/volume forms, named X forms and the pane-ratio control. Grouped role arrays are authored through the model/API. Close-selector transitions drop redundant helper X overrides so the pair inherits its new owner axis; fixed Y role overrides and unrelated settings are preserved. Saved state, source/full-domain metadata, raw values and source IDs retain the existing financial contracts. Additional panes and per-pair or named price/volume Y axes remain separate capabilities.

Line, smooth line, stepped line, area, stepped area, stacked area and normalized area charts, plus the line/area layers of column-line and area-line combinations, keep every accepted source observation reachable with one chart inspection Tab stop. This is enabled automatically; there is no saved option or additional inspector control. It applies in standalone Charts and Pivot through the shared renderer.

KeyAction
Tab / Shift+TabEnter or leave the chart’s single inspection stop; other chart controls retain their own stops.
Right / DownNext source observation or inspection mark; wraps at the end.
Left / UpPrevious observation or mark; wraps at the beginning.
Home / EndFirst / last logical inspection item in the chart.
Enter / SpaceActivate the canonical source datum using the configured drill/selection behavior. Derived/synthetic observations remain inspection-only.
EscapeDismiss tooltip and transient highlights; in selection mode also invoke the existing selection-clear behavior. Keyboard focus remains visible.

Traversal follows layer/series registration order, then each path’s source X order. Numeric/time X uses ordered observations; axis reversal changes positions without reversing that order. Mixed combinations retain other layers’ existing mark order. Missing/nonfinite values, hidden series and observations outside the accepted X/Y plot domain are excluded. Clipped/interpolated boundary vertices are never fabricated as source observations.

markers: false, markers: 'start' / 'end', runtime marker predicates and all path sampling modes affect paint density, while keyboard traversal still reaches eligible source observations. It uses raw canonical values for accessible names, tooltips, hover, underlying records and activation, including on normalized or stacked areas. Traversal does not rerun marker predicates or recreate regular source glyphs. Transient highlight glyphs retain their existing runtime element styling.

Rendered eligible glyphs are reused. An omitted or visually unavailable glyph uses a single temporary focus-ring inspection target per SVG, rather than hidden DOM for every observation. The ring is independent of chartOptions.disableLineItemHighlight and seriesOverrides[id].disableHighlight, and excluded from element customization and SVG/image/PDF export snapshots. The ordinary decorative hover highlight remains separate and cannot activate data. Runtime formatters still apply to the inspected source values.

Updates and resizes restore focus by series/category identity, including after reordering or value changes; removal or clipping chooses a nearby eligible observation in the same series where possible. Updates never take focus away from another control. Blur hides the temporary ring; destruction discards its node and listeners. Preview charts remain noninteractive. Static SVG may contain one transparent Tab-entry target; hydration restores runtime interactions, not previous browser focus. No function or interaction state is added to saved models.

Pointer path inspection still chooses the nearest source X observation; continuous curve-distance hit detection is a separate capability.

For bound sample/metric groups, named X/Y assignments, scoped navigation/readouts, complete remote partitions and Pivot data views, see histogram collections.

Radar line and area share independent numeric metric domains/scales, reversible projection, authored grid bands and value labels through axes.radar and radar appearance options. See radar axis contracts and Pivot editing.

Axis support follows each chart family’s capabilities described above. Named axes, scale transforms and tick policies use the same contracts in standalone Charts and Pivot. Keep raw data values when configuring bounds, navigation and annotations; projection applies the selected scale and direction.

See value-axis navigation for primary/secondary/named Y windows and scale-compatible limits.

Grouped columns/bars and scatter can opt into chartOptions.rendererPolicy: 'batched-svg' or 'webgl'. Heatmap additionally accepts 'webgl'; see the GPU capability and fallback matrix. Omission keeps individual SVG. See batched SVG rendering for the supported families, keyboard/pointer inspection, saved policy and deterministic customization fallbacks.

Axis options participate in saved models and server-rendered SVG. Await chart.update() before reading the updated model, and preserve the other axis settings when replacing an axis configuration. Destroy the chart when its host unmounts to release its controls, listeners and rendered content.

For bubble/sample-owned X and complete histogram bins in mixed Cartesian layers, see bubble and histogram combinations.

See measured polar axis layout for named-axis gutters, collision omission and the runtime placement callback.