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, where 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, which is 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, which makes 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.