Skip to content

Axis bounds

min and max set an axis domain outright. Values outside the range are clipped at the plot edge rather than allowed to overflow, and a band marks each edge that is hiding something.

ts
// min and max set the axis domain outright. The 1400ms spike sits well outside
// max, so it is clipped to the plot instead of flattening everything else, and
// the clip indicator band marks the edge that is hiding it.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'p95 Response Time' },
  categoryAxis: { property: 'time', type: 'string', scale: 'ordinal' },
  valueAxes: [{ id: 'VA0', title: { text: 'Response time (ms)' }, min: 0, max: 200 }],
  series: [
    {
      property: 'ms',
      title: 'p95',
      renderer: 'line',
      marker: { shape: 'circle' }
    }
  ]
};

export const data = [
  { time: '09:00', ms: 118 },
  { time: '09:05', ms: 124 },
  { time: '09:10', ms: 109 },
  { time: '09:15', ms: 132 },
  { time: '09:20', ms: 1408 },
  { time: '09:25', ms: 141 },
  { time: '09:30', ms: 116 },
  { time: '09:35', ms: 127 },
  { time: '09:40', ms: 105 },
  { time: '09:45', ms: 119 }
];

How it works

  • min/max are hard bounds: the domain becomes exactly the range given, whatever the data does. One outlier no longer flattens the rest of the series into a band at the bottom of the plot.
  • A clipped mark keeps the part that is inside. The line above enters and leaves the top edge rather than vanishing for that category, so a hidden value never reads as missing data.
  • To keep a value in view without ever clipping, use softMin / softMax instead. They extend the domain to the bound when the data does not already reach it, and give way when it does — see positive and negative for holding zero in view that way.
  • Both bounds default to auto, which fits the domain to the data (plus minMarginFraction / maxMarginFraction). An auto end never clips on its own.
  • minOffset / maxOffset shift an auto end by a fixed amount once the data has been fitted, which is how you pad or tighten a domain without pinning it to a number. Shifting a bound inward hides data, so an offset end clips like an explicit one and gets the same indicator band. They apply only to an end left on auto.
  • min must not be above max when both are set; the config is rejected otherwise. To run an axis backwards use reversed, below.
  • The same properties exist on the category axis, on any scale but ordinal, where they window a numeric or date range (a date bound is an ISO string or a millisecond timestamp).
  • plot.clipOverflow sets how far (in pixels) marks may spill past each edge before being cut. Raise it when a marker or a thick stroke sitting on the boundary is being shaved.

Marking what is hidden

The band comes from the clip indicator, which draws on every plot edge with data behind it and needs no configuration to appear.

  • label sets the text, which doubles as the band's accessible name and shows on hover. Set it to null for a band with no text.
  • size is the band depth, defaulting to auto — the label height plus labelPadding on both sides.
  • hatch sets the diagonal fill's spacing and lineWidth. Set it to null for a flat fill instead, which also lightens the style default, since a solid band at the hatched weight reads much heavier.
  • Two axes clipping the same edge produce one band. Bands on neighbouring edges meet on a diagonal, so no corner is drawn twice.
  • visible: false turns the band off. Clipping itself still happens — the values are hidden either way, so leaving it on is what tells a reader they are.

Reversing an axis

ts
// reversed flips which end of the axis each bound sits at. A rank reads best
// with first place at the top, which is the opposite of the default direction.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'League Position' },
  categoryAxis: { property: 'week', type: 'string', scale: 'ordinal' },
  valueAxes: [{ id: 'VA0', title: { text: 'Position' }, reversed: true, min: 1, max: 10, tickLabel: { format: 'd' } }],
  series: [
    {
      property: 'position',
      title: 'Position',
      renderer: 'line',
      marker: { shape: 'circle' }
    }
  ]
};

export const data = [
  { week: 'Wk 1', position: 8 },
  { week: 'Wk 2', position: 7 },
  { week: 'Wk 3', position: 9 },
  { week: 'Wk 4', position: 5 },
  { week: 'Wk 5', position: 4 },
  { week: 'Wk 6', position: 6 },
  { week: 'Wk 7', position: 3 },
  { week: 'Wk 8', position: 2 }
];
  • reversed runs an axis backwards. min is still the lower bound — the flag changes which end of the plot that bound sits at, so a rank of 1 lands at the top.
  • It works on every axis, including the category axis and ordinal scales, where it reverses the slot order.
  • It composes with plot.inverted, which is a different setting: inverted swaps which screen direction each axis runs along (see horizontal charts), while reversed flips one axis end for end.
  • Base lines, thresholds, ticks and stacking are unaffected — the domain still ascends, only its screen direction changes.

Released under the MIT License.