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',
      label: {
        format: ',.0f',
        position: 'inside',
        // Only the colors of the normal state are overridden — the opacities,
        // the stroke width, and the focused/defocused states keep their defaults.
        textStyle: { normal: { strokeColor: '#ffffff', fillColor: '#ffffff' } },
        minRangeFraction: 0.05
      }
    },
    {
      property: 'shipped',
      title: 'Shipped',
      renderer: 'line',
      marker: {
        shape: 'circle',
        size: 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

  • marker.shape picks from circle, cross, diamond, square, star, triangle and wye; line, area and none series default to circle, bars to null (no marker). marker.size sets the size (default 6px) and marker.style styles it — stroke and fill colors, opacities and widths per normal/focused/ defocused state. Point markerProperty at a data property to scale marker size per value — see bubbles below.
  • Labels come from labelProperty — point it at the series' own property (as above) for value labels, or at any other data property. label.format formats the value ("auto" derives a format from the data), and label.prefix / label.suffix wrap it with text a d3 format can't express, such as a unit. They are separate from the tooltip's valuePrefix / valueSuffix because a label may show a different property than the series value.
  • label.position places labels inside, center (the default) or outside the shape, and label.offset nudges every label by a fixed pixel amount along the value axis.
  • Three fraction guards hide labels that wouldn't fit: label.minRangeFraction (used above — it hides labels on bars shorter than 5% of the axis extent), and label.minPositionFraction / label.maxPositionFraction, which hide labels whose values sit too close to the value axis base or too close to the domain end they run toward, each by a fraction of the domain extent. Where the axis has no base, the guards use the domain minimum as the base. base defaults to 0 on any axis with stacks.
  • label.position, label.offset and the two position-fraction guards each have a variant under label.aboveBase / label.belowBase (label.aboveBase.position, label.belowBase.offset, …) that apply only to values above or below the value axis base — handy for labeling positive and negative bars differently. Their default 'auto' inherits the plain setting, except the below-base offset, which inherits the negated label.offset so both sides shift the same distance in opposite directions.
  • label.textStyle styles 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 label.textStyle.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: { text: 'Requests per second' },
    property: 'load',
    type: 'number',
    scale: 'linear'
  },
  chart: {
    margin: { right: 5 }
  },
  valueAxes: [{ id: 'VA0', title: { text: 'Latency (ms)' } }],
  seriesDefaults: { renderer: 'none' },
  series: [
    {
      property: 'v1',
      title: 'v1',
      marker: {
        shape: 'circle',
        size: 6
      }
    },
    {
      property: 'v2',
      title: 'v2',
      marker: {
        shape: 'diamond',
        minSize: 4,
        size: 16
      },
      markerProperty: 'v2Errors'
    }
  ]
};

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 marker.minSize (default 1px) and marker.size with the property's value.
  • marker.sizeScale 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 marker.minSize 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 MIT License.