AnyChart Integration

MAIDR makes AnyChart charts accessible through audio sonification, text descriptions, braille output, and keyboard navigation. The adapter exposes a one-line binder you call after the chart has been drawn.

Quick Start

Load AnyChart, MAIDR's core runtime, and the AnyChart adapter, then call bindAnyChart():

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My AnyChart Chart</title>
    <!-- 1. Load AnyChart -->
    <script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-base.min.js"></script>
    <!-- 2. Load the MAIDR core runtime -->
    <script src="https://cdn.jsdelivr.net/npm/maidr/dist/maidr.js"></script>
  </head>
  <body>
    <div id="container" style="width: 700px; height: 500px"></div>

    <!-- 3. Load the AnyChart adapter and bind your chart -->
    <script type="module">
      import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

      document.addEventListener('DOMContentLoaded', () => {
        const chart = anychart.bar([
          ['Mon', 20], ['Tue', 14], ['Wed', 23], ['Thu', 25], ['Fri', 22],
        ]);
        chart.title('Tips by Day');
        chart.xAxis().title('Day');
        chart.yAxis().title('Count');
        chart.container('container').draw();

        // One line — extracts data, sets maidr-data, fires init event.
        bindAnyChart(chart, {
          id: 'tips-by-day',
          title: 'Tips by Day',
          axes: { x: 'Day', y: 'Count' },
        });
      });
    </script>
  </body>
</html>

Once the page loads, click on the chart (or Tab to it) and MAIDR activates with:

How It Works

The adapter is a small wrapper around AnyChart's public API:

  1. Inspection — walks the chart's series via chart.getSeriesCount() / chart.getSeriesAt(i) to identify series types
  2. Extraction — iterates each series with series.getIterator() and converts the rows into MAIDR's accessibility schema
  3. Binding — writes a maidr-data attribute onto the chart container and dispatches the maidr:bindchart event so MAIDR initializes the chart

AnyChart must be loaded separately — the adapter does not bundle the AnyChart library. Always call bindAnyChart() after chart.draw(); the series data and SVG container only become available once the chart is rendered.

Supported Chart Types

MAIDR Type AnyChart Series Example
Bar bar, column Bar chart
Line line, spline, step-line, area, step-area, spline-area Line chart
Scatter scatter, marker, bubble Scatter plot
Box Plot box Box plot
Heatmap heatmap, heat Heatmap
Candlestick candlestick, ohlc Candlestick

Area series are represented as line traces — the filled-area visual is lost in the accessible representation. A console warning is emitted when this downgrade occurs.

Notes on chart-type detection:

Code Examples

Bar Chart

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  const chart = anychart.bar([
    ['Sat', 87], ['Sun', 76], ['Thu', 62], ['Fri', 19],
  ]);
  chart.title('The Number of Tips by Day');
  chart.xAxis().title('Day');
  chart.yAxis().title('Count');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'tips-bar',
    title: 'The Number of Tips by Day',
    axes: { x: 'Day', y: 'Count' },
  });
</script>

Multi-dataset Bar Charts

Adding multiple bar series to the same chart produces a dodged (grouped) bar trace in MAIDR — each series becomes its own row of bars, navigable with up / down arrows:

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  const chart = anychart.bar();
  chart.bar([['Mon', 20], ['Tue', 14], ['Wed', 23]]).name('Lunch');
  chart.bar([['Mon', 12], ['Tue', 19], ['Wed', 15]]).name('Dinner');
  chart.title('Tips by Meal');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'tips-grouped',
    title: 'Tips by Meal',
    axes: { x: 'Day', y: 'Count' },
  });
</script>

Each chart.bar(...) call adds one series. The adapter walks every series via chart.getSeriesCount() / chart.getSeriesAt(i) and emits one MAIDR layer per series. Series names (set via .name('Lunch')) become the layer titles announced to screen readers.

Line Chart

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  const chart = anychart.line([
    ['Mon', 4.2], ['Tue', 5.1], ['Wed', 6.3], ['Thu', 5.8],
    ['Fri', 7.4], ['Sat', 8.9], ['Sun', 7.7],
  ]);
  chart.title('Average Daily Sales');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'sales-line',
    title: 'Average Daily Sales',
    axes: { x: 'Day', y: 'Sales (thousands)' },
  });
</script>

Scatter Plot

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  const chart = anychart.scatter();
  chart.marker([
    { x: 1.1, value: 2.3 }, { x: 2.4, value: 3.9 },
    { x: 3.0, value: 5.2 }, { x: 4.5, value: 4.4 },
    { x: 5.2, value: 6.8 }, { x: 6.7, value: 7.1 },
  ]);
  chart.title('Sample Scatter Plot');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'sample-scatter',
    title: 'Sample Scatter Plot',
    axes: { x: 'X Value', y: 'Y Value' },
  });
</script>

Box Plot

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  // Rows: [x, lowest, q1, median, q3, highest]
  const chart = anychart.box([
    ['Set A', 760, 801, 848, 895, 965],
    ['Set B', 733, 853, 939, 980, 1080],
    ['Set C', 714, 762, 817, 870, 918],
  ]);
  chart.title('Distribution Summary');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'dist-box',
    title: 'Distribution Summary',
    axes: { x: 'Group', y: 'Value' },
  });
</script>

Note: AnyChart's iterator API does not expose outlier arrays. The MAIDR representation reports only the five-number summary (min, Q1, median, Q3, max).

Heatmap

<!-- Heatmap is a separate AnyChart module on top of anychart-base -->
<script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-heatmap.min.js"></script>

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  const data = [
    { x: 'Math', y: 'GPT-4', heat: 92 },
    { x: 'Math', y: 'Claude', heat: 89 },
    { x: 'Code', y: 'GPT-4', heat: 88 },
    { x: 'Code', y: 'Claude', heat: 91 },
  ];

  const chart = anychart.heatMap(data);
  chart.title('Model Scores by Task');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'scores-heat',
    title: 'Model Scores by Task',
    axes: { x: 'Task', y: 'Model' },
  });
</script>

Candlestick

<!-- Candlestick is provided by the financial / stock module -->
<script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-stock.min.js"></script>

<div id="container" style="width: 700px; height: 400px"></div>
<script type="module">
  import { bindAnyChart } from 'https://cdn.jsdelivr.net/npm/maidr/dist/anychart.mjs';

  // Rows: [x, open, high, low, close]
  const chart = anychart.candlestick([
    ['2024-01-01', 100, 110,  95, 108],
    ['2024-01-02', 108, 115, 105, 112],
    ['2024-01-03', 112, 113, 100, 102],
    ['2024-01-04', 102, 108,  98, 106],
  ]);
  chart.title('Daily OHLC Prices');
  chart.container('container').draw();

  bindAnyChart(chart, {
    id: 'ohlc-candle',
    title: 'Daily OHLC Prices',
    axes: { x: 'Date', y: 'Price (USD)' },
  });
</script>

Binder Options

bindAnyChart(chart, {
  id?: string;                                     // defaults to container element's id
  title?: string;                                  // defaults to chart.title().text()
  axes?: { x?: string; y?: string };               // override axis labels
  selectors?: Array<string | string[] | undefined>; // per-series CSS selectors for SVG highlighting
});

The selectors Option

For bar, line, scatter, box, heatmap, and candlestick charts the adapter auto-discovers the SVG elements it needs to highlight (see How Highlighting Works (Advanced) below). For charts where the heuristics fall short — or to override them — pass explicit CSS selectors:

// 1. Per-series selectors. Each array entry maps to a series by index.
bindAnyChart(chart, {
  selectors: ['.series-0 rect', '.series-1 rect'],
});

// 2. Skip highlighting for a specific series with `undefined`.
bindAnyChart(chart, {
  selectors: ['.series-0 rect', undefined], // series 1 gets no highlight
});

// 3. Single-element array — applied to every series.
bindAnyChart(chart, {
  selectors: ['.chart rect'],
});

// 4. Per-point selectors via nested string[] (one entry per data point).
bindAnyChart(chart, {
  selectors: [['#bar-mon', '#bar-tue', '#bar-wed']],
});

Selectors are resolved against the chart container, so they may be scoped relatively (e.g. '.series-0 rect' rather than '#container .series-0 rect').

How Highlighting Works (Advanced)

AnyChart's SVG output uses opaque, internally-generated ids (ac_path_*, ac_rect_*, ac_layer_*) and does not expose stable CSS classes. To still provide reliable per-point highlighting without forcing every consumer to write selectors, bindAnyChart() stamps stable data-maidr-anychart-* attributes onto the relevant SVG elements after the chart renders:

Chart type Attribute Value format
Bar / column data-maidr-anychart-bar "<seriesIndex>-<pointIndex>"
Line / area / spline data-maidr-anychart-line-point "<seriesIndex>-<pointIndex>"
Scatter / marker / bubble data-maidr-anychart-scatter-point "<seriesIndex>-<pointIndex>"
Box plot data-maidr-anychart-box "<seriesIndex>-<pointIndex>"
Heatmap data-maidr-anychart-heatmap-cell "<rowIndex>-<colIndex>"
Candlestick / OHLC data-maidr-anychart-candlestick-cell "<seriesIndex>-<pointIndex>"

The adapter's generated selectors then target those attributes (e.g. [data-maidr-anychart-bar="0-3"]), which keeps highlighting stable across re-renders.

Layer discrimination

For heatmap and candlestick charts, the stamping logic must find the correct <g> layer holding the data paths. AnyChart applies a clip-path="url(#ac_clip_*)" attribute to series-data layers so that rendering is clipped to the plot area; chart-level layers (axes, gridlines, background) are intentionally unclipped so they can paint outside the plot area (axis labels, ticks, title space).

The adapter uses this clip-path presence as the primary discriminator when picking the series layer. Without it, the chosen layer can end up being the axes/background group — whose children outnumber the actual data paths — producing off-by-one highlighting (the first highlight covers the background and the last data point is missed). Defense-in-depth path filters also skip:

If you want to bypass auto-stamping entirely, pass explicit selectors — they always take precedence over the generated data-maidr-anychart-* selectors.

Keyboard Controls

Once a chart is focused, use standard MAIDR keyboard shortcuts:

Function Key (Windows) Key (Mac)
Move between data points Arrow keys Arrow keys
Go to extremes Ctrl + Arrow Cmd + Arrow
Toggle Sonification S S
Toggle Braille Mode B B
Toggle Text Mode T T
Toggle Review Mode R R
Auto-play Ctrl + Shift + Arrow Cmd + Shift + Arrow
Stop Auto-play Ctrl Cmd

For the full list, see the Keyboard Controls reference.

Integration Comparison

Feature Vanilla JS (CDN) React Component AnyChart Adapter
Setup maidr-data attribute with JSON data prop on <Maidr> bindAnyChart(chart, opts) after draw()
Data source Manual JSON schema Manual JSON schema Auto-extracted from AnyChart series
SVG selectors Manual CSS selectors Manual CSS selectors Optional selectors option
Configuration Required Required Minimal — id, title, axes only
Chart types All MAIDR types All MAIDR types 6 AnyChart families
Dynamic charts Manual init React lifecycle Re-call bindAnyChart() after redraw

npm Installation (Optional)

For bundler-based projects:

npm install maidr anychart
import anychart from 'anychart';
import 'maidr'; // loads the core runtime (registers maidr:bindchart listener)
import { bindAnyChart } from 'maidr/anychart';

const chart = anychart.bar([['Mon', 20], ['Tue', 14], ['Wed', 23]]);
chart.container('container').draw();

bindAnyChart(chart, {
  id: 'my-chart',
  title: 'Tips by Day',
  axes: { x: 'Day', y: 'Count' },
});

Both maidr and maidr/anychart ship ESM modules with TypeScript declarations.

Advanced: Manual Data Extraction

In addition to the one-line bindAnyChart() binder, the adapter exports a lower-level anyChartToMaidr() function that returns the generated MAIDR JSON without touching the DOM or dispatching any events:

import { anyChartToMaidr } from 'maidr/anychart';

const chart = anychart.bar([['Mon', 20], ['Tue', 14], ['Wed', 23]]);
chart.container('container').draw();

const maidrData = anyChartToMaidr(chart, {
  id: 'tips-bar',
  title: 'Tips by Day',
  axes: { x: 'Day', y: 'Count' },
});
// maidrData is a Maidr JSON object, or null if no convertible series found.

Useful when you want to:

anyChartToMaidr() accepts the same options as bindAnyChart() (id, title, axes, selectors) and returns the same Maidr | null shape that the binder produces internally — the binder just additionally writes the JSON to the container's maidr-data attribute and fires the maidr:bindchart event.

API Documentation

For the complete TypeScript API reference, see the API Documentation.