Skip to content

Markers and labels

Markers draw a shape at each value of a series; labels render a data value next to each shape. Both are per-series config.

ts
// Markers draw a shape at each value of a line series; labels render the
// value of labelProperty next to each shape — point it at the series'
// own property to show value labels.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'Release Velocity' },
  categoryAxis: { property: 'sprint', type: 'string', scale: 'ordinal' },
  series: [
    {
      property: 'planned',
      title: 'Planned',
      renderer: 'bar',
      labelProperty: 'planned',
      labelFormat: ',.0f',
      labelPosition: 'inside',
      // Only the colors of the normal state are overridden — the opacities,
      // the stroke width, and the focused/defocused states keep their defaults.
      labelTextStyle: { normal: { strokeColor: '#ffffff', fillColor: '#ffffff' } },
      labelMinRangeFraction: 0.05
    },
    {
      property: 'shipped',
      title: 'Shipped',
      renderer: 'line',
      markerShape: 'circle',
      markerSize: 5
    }
  ]
};

export const data = [
  { sprint: 'S1', planned: 12, shipped: 9 },
  { sprint: 'S2', planned: 14, shipped: 13 },
  { sprint: 'S3', planned: 11, shipped: 12 },
  { sprint: 'S4', planned: 15, shipped: 14 },
  { sprint: 'S5', planned: 13, shipped: 11 }
];

How it works

  • markerShape picks from circle, cross, diamond, square, star, triangle, wye; markerSize sets its size and markerStyle paints it — a normal/focused/defocused set of stroke and fill colors, opacities and widths. Point markerProperty at a data property to scale marker size per value — bubble charts.
  • Labels come from labelProperty — point it at the series' own property (as above) for value labels, or at any other data property. labelFormat formats the value ("auto" derives from the data).
  • labelPosition places labels inside, center, or outside the shape, and labelOffset nudges every label by a fixed pixel amount along the series axis.
  • Three fraction guards hide labels that wouldn't fit: labelMinRangeFraction (used above — it hides labels on bars shorter than 5% of the axis), and labelMinPositionFraction / labelMaxPositionFraction, which hide labels whose values sit too close to the domain's minimum or maximum.
  • labelPosition, labelOffset, and the two position-fraction guards each have labelAboveBase* / labelBelowBase* variants (labelAboveBasePosition, labelBelowBaseOffset, …) that override them only for values above or below the series base — handy for labeling positive and negative bars differently. Their default 'auto' inherits the base knob.
  • labelTextStyle paints the label text, again per focus state. Its colors accept the palette modes (series, seriesIndex, categoryIndex) as well as literal colors — see colorPalette. The example above sets only labelTextStyle.normal.strokeColor and .fillColor; every other member, including both other states, keeps its default.

Scatter and bubble charts

Markers on their own make a scatter chart: set renderer to none so a series draws no shape, and only its markers remain.

ts
// A scatter chart is marker-only series (renderer 'none') on a linear category
// axis, so points sit at their measured x values. Point markerProperty at a
// data property to scale marker size per point — a bubble chart.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'Latency under Load' },
  categoryAxis: {
    title: 'Requests per second',
    property: 'load',
    type: 'number',
    scale: 'linear'
  },
  chart: {
    margin: { right: 5 }
  },
  valueAxes: [{ id: 'VA0', title: 'Latency (ms)' }],
  seriesDefaults: { renderer: 'none' },
  series: [
    {
      property: 'v1',
      title: 'v1',
      markerShape: 'circle',
      markerSize: 6
    },
    {
      property: 'v2',
      title: 'v2',
      markerShape: 'diamond',
      markerProperty: 'v2Errors',
      markerMinSize: 4,
      markerSize: 16
    }
  ]
};

export const data = [
  { load: 12, v1: 38, v2: 31, v2Errors: 0 },
  { load: 45, v1: 42, v2: 33, v2Errors: 1 },
  { load: 70, v1: 55, v2: 41, v2Errors: 2 },
  { load: 160, v1: 74, v2: 52, v2Errors: 3 },
  { load: 240, v1: 92, v2: 60, v2Errors: 8 },
  { load: 310, v1: 121, v2: 71, v2Errors: 14 },
  { load: 470, v1: 168, v2: 95, v2Errors: 25 }
];
  • Use a linear category axis scale (with number or date type) so points are positioned by their measured x values rather than evenly spaced category slots.
  • For bubbles, point markerProperty at a data property; marker sizes scale between markerMinSize and markerSize with the property's value.
  • markerSizeScale picks how they scale: the default sqrt scales each marker's area with its value — the way readers judge bubble magnitude — while linear scales its diameter, which visually exaggerates differences. The markerMinSize floor keeps the smallest bubble visible (and hoverable); for exactly value-proportional areas, set it to 0 on data whose minimum is 0.
  • Every series reads its x from the row's category value, so series share x positions. For series with points at different x values, give each x its own row and leave the other series' properties out — a row draws a marker only for the series that have a value there.

Released under the BSD-3-Clause License.