Skip to content

Legend

The legend lists the series with their icons, and is where series get filtered in and out of the chart. Its config covers where it sits (position, align, alignedToAxes), how it is drawn (backgroundStyle, item, icon) and what clicking and hovering its items do.

ts
// A styled legend above the plot: right-aligned against the chart bounds
// instead of the axes, boxed with a faint background, and with filtered
// series struck through rather than only greyed out. The long EMEA title is
// truncated once the legend row runs out of room.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'Regional Revenue' },
  categoryAxis: { property: 'quarter', type: 'string', scale: 'ordinal' },
  valueAxes: [{ id: 'VA0', title: { text: 'Revenue ($k)' }, min: 0 }],
  seriesDefaults: { renderer: 'line' },
  series: [
    { property: 'north', title: 'North America' },
    { property: 'emea', title: 'Europe, Middle East and Africa' },
    { property: 'apac', title: 'Asia Pacific' },
    { property: 'latam', title: 'Latin America' }
  ],
  legend: {
    position: 'top',
    align: 'right',
    alignedToAxes: false,
    margin: { top: 0, right: 0, bottom: 8, left: 0 },
    padding: { top: 2, right: 6, bottom: 2, left: 6 },
    backgroundStyle: {
      strokeColor: 'currentColor',
      strokeOpacity: 0.25,
      strokeWidth: 1,
      fillColor: 'currentColor',
      fillOpacity: 0.04
    },
    item: {
      margin: { top: 1, right: 4, bottom: 1, left: 4 },
      padding: { top: 2, right: 4, bottom: 2, left: 4 },
      textStyle: { fillColor: 'currentColor', fillOpacity: 0.85 }
    },
    icon: { size: 10, spacing: 6 },
    strikeThroughFiltered: true,
    truncation: { enabled: true, text: '…' }
  }
};

export const data = [
  { quarter: 'Q1', north: 420, emea: 310, apac: 180, latam: 95 },
  { quarter: 'Q2', north: 455, emea: 335, apac: 210, latam: 110 },
  { quarter: 'Q3', north: 470, emea: 320, apac: 245, latam: 120 },
  { quarter: 'Q4', north: 510, emea: 360, apac: 270, latam: 140 }
];

How it works

  • position puts the legend above (top) or below (bottom, the default) the plot, and align places it left, center or right within its row. alignedToAxes chooses what that row is: the plot width between the axes (true, the default) or the full chart width (false), which is why the legend above lines up with the chart edge rather than the value axis. Items that do not fit the row wrap onto further rows.
  • visible defaults to true only above one series — a single-series chart has to set it, as the next example does.
  • margin and padding space the legend as a whole from the title and plot; backgroundStyle fills and outlines the padded box (the margin lies outside it). Its default stroke and fill opacities are 0, so set them to make the box show.
  • item styles each series entry the same way: item.margin and item.padding around the icon and title, item.backgroundStyle behind them and item.textStyle for the title text, which follows the host page's colour through currentColor by default. The padded item box is also the click target.
  • icon configures the swatch: size (auto matches the text height), spacing to the title, borderStyle, and whether it shows the series colour, its marker shape or a placeholder in unfilteredColor / filteredColor.
  • truncation.enabled (on by default) cuts a title that is wider than the legend has room for and appends truncation.text.
  • Clicking an item filters its series out and back in while filterOnClick is on (the default); a filtered item's icon switches to icon.filteredColor (transparent by default), and strikeThroughFiltered strikes its title through as well. Hovering an item focuses its series while focusOnHover is on (the default), and focusOnClick — off by default — makes a click do the same, which is what touch users need since a tap never counts as a hover.

A single-series key

With one series there is nothing to filter or focus against, so this legend is a plain key: shown explicitly, and with all three interactions switched off so its item does not respond to the pointer.

ts
// One series: the legend only defaults to visible above one series, so
// visible is set explicitly. With filtering and focus switched off it is a
// plain key — clicking or hovering the item does nothing — and the icon
// drops its border to read as a colour swatch.
import type { MochartInputConfig } from '@mochart/core';

export const config: MochartInputConfig = {
  version: '1.0.0',
  title: { text: 'Daily Sign-ups' },
  categoryAxis: { property: 'day', type: 'string', scale: 'ordinal' },
  valueAxes: [{ id: 'VA0', title: { text: 'Sign-ups' }, min: 0 }],
  series: [{ property: 'signups', title: 'New sign-ups', renderer: 'bar' }],
  legend: {
    visible: true,
    position: 'bottom',
    align: 'left',
    alignedToAxes: true,
    filterOnClick: false,
    focusOnClick: false,
    focusOnHover: false,
    icon: { size: 12, borderStyle: { strokeOpacity: 0 } }
  }
};

export const data = [
  { day: 'Mon', signups: 34 },
  { day: 'Tue', signups: 41 },
  { day: 'Wed', signups: 38 },
  { day: 'Thu', signups: 52 },
  { day: 'Fri', signups: 47 },
  { day: 'Sat', signups: 23 },
  { day: 'Sun', signups: 19 }
];
  • Setting visible: true is what makes the legend appear here; leaving it unset hides it whenever the chart has one series.
  • With filterOnClick, focusOnClick and focusOnHover all false the item is inert, and the legend leaves the keyboard tab order as well.
  • The default bottom position and alignedToAxes: true line the legend up with the plot, so align: 'left' starts it under the value axis edge.

Released under the MIT License.