Skip to content

Chart.js Accessibility — Making Charts Usable for Everyone

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Chart.js Accessibility. We cover key concepts, practical examples, and best practices to help you master this topic.

Chart.js accessibility ensures charts are usable by people with visual, motor, and cognitive disabilities through ARIA attributes, keyboard navigation, and fallback data tables.

What You'll Learn

By the end of this guide, you will add ARIA labels and descriptions to charts, implement keyboard navigation for data points, use high-contrast color schemes, generate accessible data tables, and test with screen readers.

ARIA Labels

var ctx = document.getElementById('myChart');
ctx.setAttribute('role', 'img');
ctx.setAttribute('aria-label', 'Bar chart showing quarterly sales for 2025');

new Chart(ctx, {
    type: 'bar',
    data: { ... },
    options: {
        responsive: true
    }
});

Fallback Data Table

function createDataTable(chart) {
    var table = document.createElement('table');
    table.setAttribute('aria-label', 'Chart data table');
    var caption = table.createCaption();
    caption.textContent = chart.canvas.getAttribute('aria-label');

    var header = table.insertRow();
    header.insertCell().textContent = 'Label';
    chart.data.datasets.forEach(function(ds) {
        header.insertCell().textContent = ds.label;
    });

    chart.data.labels.forEach(function(label, i) {
        var row = table.insertRow();
        row.insertCell().textContent = label;
        chart.data.datasets.forEach(function(ds) {
            row.insertCell().textContent = ds.data[i];
        });
    });

    document.getElementById('data-table-container').appendChild(table);
}

High Contrast Colors

var accessibleColors = ['#006bb3', '#e67e00', '#2d6a4f', '#9b2226', '#6c4f3d'];

new Chart(ctx, {
    type: 'bar',
    data: {
        datasets: [{
            backgroundColor: accessibleColors[0]
        }]
    }
});

Common Mistakes

1. Missing ARIA Labels on Canvas

Canvas elements are invisible to screen readers by default. Always set role and aria-label.

2. Relying Only on Color

Color-only encoding excludes color-blind users. Add patterns, labels, or different shapes.

3. No Keyboard Navigation

Users with motor impairments may not use a mouse. Provide tabIndex and keyboard event handlers.

4. Low Contrast Text

Light gray text on white background fails WCAG contrast ratio. Use minimum 4.5:1 contrast.

5. No Data Table Fallback

Screen readers struggle with complex chart data. Provide an HTML data table alongside the chart.

Practice Questions

Q1: What ARIA attribute makes a canvas accessible? A: role="img" and aria-label with a description of the chart content.

Q2: What is the minimum WCAG contrast ratio for text? A: 4.5:1 for normal text, 3:1 for large text (18px+).

Q3: How do you make chart elements keyboard-navigable? A: Set tabIndex on canvas and handle keydown events for Enter and Arrow keys.

Q4: What is a fallback data table? A: An HTML table containing the same data as the chart, hidden visually but available to screen readers.

Q5: How do you test chart accessibility? A: Use a screen reader (VoiceOver, NVDA), axe DevTools, and Lighthouse accessibility audit.

Challenge: Build a chart with all accessibility features: ARIA labels, keyboard navigation that highlights data points on focus, a visible data table, and a toggle for high-contrast mode.

FAQ

Can I use ARIA with Chart.js?

Yes. Set ARIA attributes on the canvas element and custom plugins for per-element accessibility.

Does Chart.js have built-in accessibility?

Chart.js provides basic canvas ARIA support. Advanced accessibility requires custom implementation.

How do I add patterns for color-blind users?

Create a custom plugin that draws SVG patterns (stripes, dots) on bar fills.

What tools check chart accessibility?

axe DevTools, Lighthouse, WAVE, and screen reader testing are the most common.

Should I hide decorative charts from screen readers?

Yes. Set aria-hidden='true' for purely decorative or redundant chart elements.

Try It Yourself

Build an accessible chart with data table.

<!DOCTYPE html>
<html>
<head>
    <title>Accessible Chart Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        table { width: 100%; max-width: 500px; border-collapse: collapse; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f5f5f5; }
        .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
    </style>
</head>
<body>
<h2>Accessible Chart</h2>
<canvas id="a11yChart" width="500" height="300" role="img" aria-label="Quarterly revenue for Fiscal Year 2025 showing Q1: 30K, Q2: 45K, Q3: 40K, Q4: 60K"></canvas>
<div id="table-container" aria-hidden="true"></div>
<script>
var ctx = document.getElementById('a11yChart');
var chart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Q1', 'Q2', 'Q3', 'Q4'],
        datasets: [{
            label: 'Revenue',
            data: [30, 45, 40, 60],
            backgroundColor: ['#006bb3', '#e67e00', '#2d6a4f', '#9b2226']
        }]
    },
    options: {
        responsive: true,
        plugins: {
            legend: { labels: { color: '#333' } }
        },
        scales: {
            x: { ticks: { color: '#333' } },
            y: { ticks: { color: '#333' } }
        }
    }
});

function buildFallbackTable() {
    var container = document.getElementById('table-container');
    var table = document.createElement('table');
    table.setAttribute('tabindex', '0');
    var caption = table.createCaption();
    caption.textContent = 'Quarterly Revenue - Fiscal Year 2025';

    var header = table.insertRow();
    header.insertCell().textContent = 'Quarter';
    chart.data.datasets.forEach(function(ds) {
        header.insertCell().textContent = ds.label + ' ($K)';
    });

    chart.data.labels.forEach(function(label, i) {
        var row = table.insertRow();
        row.insertCell().textContent = label;
        chart.data.datasets.forEach(function(ds) {
            row.insertCell().textContent = ds.data[i];
        });
    });

    container.appendChild(table);
}

buildFallbackTable();
</script>
</body>
</html>

What's Next

Build a complete dashboard project.

Project — Full dashboard project.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro