Google Charts Integration
MAIDR provides an adapter for Google Charts that converts your charts into accessible, navigable visualizations with audio sonification, text descriptions, and braille output.
Quick Start
Add maidr.js and google-charts.js after your Google Charts loader. Use the adapter in the chart's ready event:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My Google Chart</title>
<!-- 1. Load Google Charts -->
<script src="https://www.gstatic.com/charts/loader.js"></script>
<!-- 2. Load MAIDR core and Google Charts adapter -->
<script src="https://cdn.jsdelivr.net/npm/maidr/dist/maidr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/maidr/dist/google-charts.js"></script>
</head>
<body>
<div id="chart" style="width: 700px; height: 500px"></div>
<script>
google.charts.load('current', { packages: ['corechart'] });
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Day', 'Tips'],
['Mon', 20],
['Tue', 14],
['Wed', 23],
['Thu', 25],
['Fri', 22],
]);
var container = document.getElementById('chart');
var chart = new google.visualization.ColumnChart(container);
// 3. Wire up MAIDR in the ready event
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(
chart,
data,
container,
{ chartType: 'ColumnChart', title: 'Tips by Day' }
);
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'Tips by Day',
legend: { position: 'none' },
});
}
</script>
</body>
</html>
Once the page loads, click on the chart (or Tab to it) and MAIDR activates with:
- Audio sonification - tones representing data values
- Text descriptions - spoken via screen readers
- Braille output - refreshable braille display support
- Keyboard navigation - arrow keys to move between data points
How It Works
The Google Charts adapter:
- Extracts data - reads the
DataTablestructure and converts it to MAIDR's schema - Marks SVG elements - identifies chart elements using Google Charts' layout API (
getBoundingBox) - Creates selectors - generates CSS selectors for visual highlighting during navigation
- Returns MAIDR config - produces a complete
Maidrobject ready for the attribute
The adapter must be called inside the chart's ready event to ensure the SVG is fully rendered.
Supported Chart Types
| Chart Type | Google Charts Class | Adapter chartType |
|---|---|---|
| Column | ColumnChart |
'ColumnChart' |
| Line | LineChart |
'LineChart' |
| Scatter | ScatterChart |
'ScatterChart' |
| Candlestick | CandlestickChart |
'CandlestickChart' |
| Stacked Column | ColumnChart + isStacked: true |
'StackedColumnChart' |
| Dodged/Grouped Column | ColumnChart (multi-series) |
'DodgedColumnChart' |
Not supported: Histogram (Google Charts API doesn't expose bin boundaries), Heatmap (not a native Google Charts type).
Code Examples
Bar/Column Chart
<div id="bar-chart"></div>
<script>
var data = google.visualization.arrayToDataTable([
['Day', 'Tips'],
['Sat', 87],
['Sun', 76],
['Thur', 62],
['Fri', 19],
]);
var container = document.getElementById('bar-chart');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'ColumnChart',
title: 'The Number of Tips by Day',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'The Number of Tips by Day',
legend: { position: 'none' },
width: 600,
height: 400,
});
</script>
Line Chart (Multi-Series)
<div id="line-chart"></div>
<script>
var data = google.visualization.arrayToDataTable([
['Year', 'Sales', 'Expenses'],
['2020', 1000, 400],
['2021', 1170, 460],
['2022', 660, 1120],
['2023', 1030, 540],
]);
var container = document.getElementById('line-chart');
var chart = new google.visualization.LineChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'LineChart',
title: 'Company Performance',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'Company Performance',
curveType: 'function',
legend: { position: 'bottom' },
width: 600,
height: 400,
});
</script>
Scatter Chart
<div id="scatter-chart"></div>
<script>
var data = google.visualization.arrayToDataTable([
['Age', 'Weight'],
[8, 12],
[4, 5.5],
[11, 14],
[4, 5],
[3, 3.5],
[6.5, 7],
]);
var container = document.getElementById('scatter-chart');
var chart = new google.visualization.ScatterChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'ScatterChart',
title: 'Age vs. Weight',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'Age vs. Weight',
hAxis: { title: 'Age' },
vAxis: { title: 'Weight' },
legend: 'none',
width: 600,
height: 400,
});
</script>
Stacked Column Chart
<div id="stacked-chart"></div>
<script>
var data = google.visualization.arrayToDataTable([
['Class', 'Did not survive', 'Survived'],
['First', 80, 136],
['Second', 97, 87],
['Third', 372, 119],
]);
var container = document.getElementById('stacked-chart');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'StackedColumnChart',
title: 'Passenger Count by Class and Survival',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'Passenger Count by Class and Survival',
isStacked: true,
legend: { position: 'bottom' },
width: 600,
height: 400,
});
</script>
Dodged/Grouped Column Chart
<div id="dodged-chart"></div>
<script>
var data = google.visualization.arrayToDataTable([
['City', '2020 Population', '2025 Population'],
['New York', 8336, 8258],
['Los Angeles', 3979, 3898],
['Chicago', 2693, 2665],
['Houston', 2320, 2314],
]);
var container = document.getElementById('dodged-chart');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'DodgedColumnChart',
title: 'City Populations (thousands)',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'City Populations (thousands)',
legend: { position: 'bottom' },
width: 600,
height: 400,
});
</script>
Candlestick Chart
<div id="candlestick-chart"></div>
<script>
// Google Charts candlestick format: [Date, Low, Open, Close, High]
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Low');
data.addColumn('number', 'Open');
data.addColumn('number', 'Close');
data.addColumn('number', 'High');
data.addRows([
[new Date(2024, 0, 1), 20, 28, 38, 45],
[new Date(2024, 0, 2), 31, 38, 30, 45],
[new Date(2024, 0, 3), 25, 31, 42, 48],
[new Date(2024, 0, 4), 35, 40, 40, 52],
[new Date(2024, 0, 5), 28, 35, 45, 55],
]);
var container = document.getElementById('candlestick-chart');
var chart = new google.visualization.CandlestickChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleChart(chart, data, container, {
chartType: 'CandlestickChart',
title: 'Stock Price by Day',
});
container.setAttribute('maidr', JSON.stringify(maidr));
});
chart.draw(data, {
title: 'Stock Price by Day',
legend: 'none',
width: 600,
height: 400,
});
</script>
Multi-Panel (Faceted) Figures
Google Charts has no native facet/trellis concept — a "faceted" page is several chart instances drawn into separate containers. createMaidrFromGoogleCharts (plural) groups those instances into one MAIDR figure: arrow keys move between panels, Enter drills into a panel's data, and Esc returns to panel navigation.
Each panel is the same { chart, dataTable, container, chartType } tuple the single-chart API takes, plus an optional title announced during panel navigation. All panel containers must live inside a single wrapper element, passed as options.root — and the combined maidr attribute is set on that wrapper, not on the individual containers. Panel containers must not be nested inside one another (e.g. don't pass a card <div> that wraps another panel's chart <div>): the adapter's container-scoped selectors would match both charts' elements, so nested containers are rejected with an error.
Because every chart fires its ready event independently, use the whenGoogleChartsReady helper to build the figure only after all panels have rendered. Register the gate before calling draw() on any chart.
<div id="facet-grid">
<div id="panel-east"></div>
<div id="panel-west"></div>
</div>
<script>
var root = document.getElementById('facet-grid');
var panels = [
{ id: 'panel-east', title: 'East', rows: [['Q1', 100], ['Q2', 200]] },
{ id: 'panel-west', title: 'West', rows: [['Q1', 80], ['Q2', 140]] },
].map(function (facet) {
var container = document.getElementById(facet.id);
return {
chart: new google.visualization.ColumnChart(container),
dataTable: google.visualization.arrayToDataTable([['Quarter', 'Revenue']].concat(facet.rows)),
container: container,
chartType: 'ColumnChart',
title: facet.title,
};
});
var charts = panels.map(function (panel) { return panel.chart; });
// Build the combined figure once ALL panels have rendered.
maidrGoogleCharts.whenGoogleChartsReady(charts, google.visualization.events, function () {
var maidr = maidrGoogleCharts.createMaidrFromGoogleCharts(panels, {
root: root,
title: 'Revenue by Region',
layout: { columns: 2 },
});
root.setAttribute('maidr', JSON.stringify(maidr));
});
panels.forEach(function (panel) {
panel.chart.draw(panel.dataTable, { legend: { position: 'none' }, width: 360, height: 260 });
});
</script>
Grid Shape
The grid shape is resolved in priority order:
- 2D array — pass
panelsasGoogleChartPanel[][]to use it directly as the subplot grid (rows may be ragged, but never empty). - Flat array +
options.layout—{ columns: n }chunks the panels row-major into rows ofn;{ rows: n }derives the column count instead. - Neither — the grid is inferred from each container's on-screen position (clustered into rows by top edge, sorted left-to-right).
Always supply panels in visual reading order (top-left panel first) so announcements match what sighted users see.
createMaidrFromGoogleCharts Options
| Option | Type | Required | Description |
|---|---|---|---|
root |
HTMLElement |
Yes | Wrapper element containing all panel containers; receives the maidr attribute |
title |
string |
No | Figure-level title announced when the figure receives focus |
id |
string |
No | Unique ID for the MAIDR instance (defaults to the root's id) |
layout |
{ rows?, columns? } |
No | Grid shape for a flat panel array (ignored for 2D arrays) |
Panel titles (e.g. the facet value, 'East') are announced in subplot summaries; per-panel axis labels come from each panel's own DataTable. Mixed chart types across panels are supported — any type from the table above works per panel.
Configuration Options
The adapter accepts a GoogleChartAdapterOptions object:
| Option | Type | Required | Description |
|---|---|---|---|
chartType |
GoogleChartType |
Yes | The chart type string (see supported types above) |
title |
string |
No | Chart title for accessibility announcements |
id |
string |
No | Unique ID for the MAIDR instance (defaults to container's id) |
Using with npm/Bundlers
For bundled projects, import the adapter directly:
import {
createMaidrFromGoogleChart,
createMaidrFromGoogleCharts,
whenGoogleChartsReady,
} from 'maidr/google-charts';
// Use in your chart's ready callback
const maidr = createMaidrFromGoogleChart(chart, dataTable, container, {
chartType: 'ColumnChart',
title: 'My Chart',
});
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.
API Documentation
For the complete TypeScript API reference, see the API Documentation.