Skip to content

The config model

A mochart config is a plain, JSON-serializable object made of per-concern sections. Every section — and almost every property inside one — is optional and falls back to a sensible default, so configs only say what differs from the defaults.

js
const config = {
  title: { … },        // chart title
  categoryAxis: { … },    // the category axis (requires `property`)
  series: [ … ],      // one entry per series (each requires `property`)
  seriesDefaults: { … },    // values shared by every series
  valueAxes: [ … ],  // one or more value axes
  legend: { … },
  tooltip: { … },
  crosshair: { … },
  animation: { … },
  // …
};

The config reference lists every section and is generated from the library's own validators, defaults, and descriptions — it can't drift from the code.

Object sections and list sections

Sections come in two shapes:

  • Object sections configure a single thing: title, categoryAxis, legend, tooltip, crosshair, animation, chart, plot, colorPalette.
  • List sections configure a collection and take an array of config objects: series, valueAxes, seriesGroups, seriesStacks, linearGradients, radialGradients. Passing a single object instead of an array is allowed and treated as a one-entry list.

Shared *Defaults sections

Every list section has a companion *Defaults section — seriesDefaults, valueAxisDefaults, and so on — whose values apply to every entry of the list. A value set on an individual entry wins over the shared one:

js
seriesDefaults: { renderer: 'bar', valueFormat: ',.0f' },
series: [
  { property: 'revenue' },                      // bar, ',.0f'
  { property: 'target', renderer: 'line' }      // line, ',.0f'
]

Styles and focus states

Everything the chart draws is styled by a style object rather than by a flat set of color properties. A style holds strokeColor, strokeOpacity and strokeWidth, plus fillColor and fillOpacity for shapes that have an interior. Lines — grid lines, tick marks, thresholds, crosshairs, error-bar whiskers — take the stroke half only.

Most elements are painted differently depending on what has focus, so their style is nested one level deeper, under normal, focused and defocused:

js
series: [{
  property: 'revenue',
  shapeStyle: {
    normal:    { fillColor: '#3366cc', fillOpacity: 0.8 },
    focused:   { fillOpacity: 1 },
    defocused: { fillOpacity: 0.3 }
  }
}]

In the focused and defocused states a color — and likewise strokeWidth and strokeDashArray — may be the literal 'same', meaning "whatever the normal state resolved to". That is the default almost everywhere: elements change opacity or width on focus but keep their color. Opacities are the exception — they are always concrete numbers, never 'same'.

Series styles additionally accept the palette modes 'series', 'seriesIndex' and 'categoryIndex' in place of a color; see colorPalette. Any style color also accepts 'currentColor' to follow the host page's CSS color (how chart chrome themes itself — see Theming and dark mode), and 'none' to switch that half of the style off.

Reference pages link to nested members with dotted anchors, so shapeStyle.normal.fillColor is addressable in its own right.

Partial overrides

Config layers are merged member by member at every depth, so a config only names what it changes. In the example above shapeStyle.normal.strokeColor, strokeWidth and both other states' colors keep their defaults — writing one member never blanks out its siblings. The same holds when a *Defaults section merges into an individual list entry.

Two values do not merge:

  • Arrays replace wholesale. ticks, gradient stops and the palette color lists are values, not structures to merge element-wise.
  • null is a real value, not a hole. { strokeColor: null } overrides a non-null default and leaves the SVG attribute unset so CSS can supply it. Use undefined (or simply omit the key) to mean "not specified".

Cross-references and id defaulting

Entries in list sections are wired together by id: a series names its value axis via axis, its stack via stack, and its series group via group, each matching an id in the corresponding section.

When exactly one target exists, the reference defaults to it — with a single valueAxes entry (or none at all) you never need to mention axis ids, and with a single seriesStacks entry every series joins that stack automatically (see the stacked bars recipe). Validation reports references that don't resolve.

Validation

Configs are validated with @mochart/movalid, producing human-readable messages rather than schema jargon:

js
import { validateConfig, getDefaults } from '@mochart/core';

const { valid, errors, warnings } = validateConfig(config, getDefaults(config));
// e.g. "series[1] - had 1 invalid properties: valueFormt"

Editor and tooling integrations can request structured locations while retaining the same validation result:

js
import { validateConfigDetailed, getDefaults } from '@mochart/core';

const { diagnostics } = validateConfigDetailed(config, getDefaults(config));
// [{
//   path: ['series', 1, 'axis'],
//   severity: 'error',
//   message: 'should equal the id property of one of the valueAxes: "missing"',
//   source: 'mochart'
// }]

path contains object keys and array indexes leading to the relevant config value. Top-level problems that cannot be assigned to one property use an empty path.

Two things validation insists on:

  • version, when present, must equal the current config format version ('1.0.0'). Omitting it means "the current format". Include it in configs you store or share — configs written against an older format can then be upgraded with migrateConfig(config).
  • Unknown properties produce warnings, and a config with warnings is rejected in strict mode — typos surface immediately instead of being silently ignored.

When a chart receives an invalid config it renders its config error state instead of a broken chart.

Enhancement

createDefaultChart validates and defaults the raw config for you on every update. The lower-level createChart expects that work done up front via enhanceConfig:

js
import { enhanceConfig } from '@mochart/core';

const mochartConfig = enhanceConfig(config);
// validated, defaults applied, *Defaults sections merged, references resolved

enhanceConfig returns a MochartConfig — the fully-built form with every default applied and cross-references resolved — which is what the renderer consumes. Data can then be checked against it with getDataErrors(mochartConfig, dataProvider) (see Data providers).

Released under the BSD-3-Clause License.