Accessible Charts & Data Visualization
A chart is a picture of data — and the accessible version of a chart is the data itself, reachable as text. This guide covers the part most teams skip: the data table that is the real text alternative, choosing between SVG, canvas, and images, labelling an SVG with role="img", never coding a series by colour alone, keeping axis labels as scalable text, keyboard-navigable interactive charts and hover-or-focus tooltips, what charting libraries actually give you, and React — mapped to WCAG 2.2.
Where Charts Lose Their Users
A data visualization does one job: it turns numbers into a shape a person can read at a glance. That is a spectacular affordance — and it is built entirely on seeing. When the reader cannot see the shape, or cannot tell your red line from your green one, or cannot reach the tooltip that holds the actual figure, the whole affordance collapses and the data is simply gone. A chart is, quietly, one of the most exclusionary things you can put on a page, because all of its meaning lives in a channel some of your users do not have.
The fix is a change of mindset before it is any line of code. Stop thinking of the picture as the content and the text as an afterthought. Flip it: the data is the content, and the chart is one especially good way of presenting it. Once the data itself is available as text — as a summary and, where it matters, as a real table — the visual becomes an enhancement layered on top, and you are free to make it as rich as you like without leaving anyone behind.
This guide is framework-agnostic and applies whether you hand- roll SVG, reach for D3, or drop in a React charting library. The requirements cluster around a handful of WCAG criteria — 1.1.1 Non-text Content for the text alternative, 1.4.1 Use of Color for the series cues, and 1.4.11 Non-text Contrast for the marks themselves — plus a few more for interactive charts. We will take them in the order you meet them when you build a chart, starting with the data.
The WCAG 2.2 Criteria Your Charts Must Satisfy
| Criterion | Level | What your chart must do |
|---|---|---|
| 1.1.1 Non-text Content | A | The chart has a text alternative that conveys its information — a short summary, and a data table or long description for the detail. |
| 1.3.1 Info and Relationships | A | The data table alternative uses real table markup — header cells, scope — so relationships between labels and values are programmatic, not visual. |
| 1.4.1 Use of Color | A | No series or category is distinguished by colour alone; a label, pattern, shape, or dash carries the same distinction. |
| 1.4.3 Contrast (Minimum) | AA | Axis labels, values, and legend text meet text-contrast ratios against their background — 4.5:1, or 3:1 for large text. |
| 1.4.5 Images of Text | AA | Labels and numbers are real text (SVG or HTML), not pixels baked into a raster image, so they scale and are machine-readable. |
| 1.4.10 Reflow | AA | The chart remains usable when zoomed to 400%; vector output scales, and a data table reflows without two-dimensional scrolling. |
| 1.4.11 Non-text Contrast | AA | Bars, lines, points, and other meaningful marks have at least 3:1 contrast against adjacent colours and the background. |
| 1.4.13 Content on Hover or Focus | AA | Tooltips that appear on hover also appear on keyboard focus, stay visible while pointed at, and can be dismissed. |
| 2.1.1 Keyboard | A | Every interaction an interactive chart offers with a mouse is also possible with the keyboard — or the data is reachable through the table. |
| 4.1.3 Status Messages | AA | Values revealed as focus moves between data points, and dynamic updates, are announced through a live region without moving focus. |
The criterion charts fail most often is 1.1.1, because the data lives only in the pixels and never reaches the accessibility tree at all. Everything else in this guide is, in one way or another, in service of getting the numbers out of the picture and into text.
1. Start With the Data, Not the Picture
Before any ARIA, decide what the text alternative is. The mistake is to reach for a single altattribute and try to cram a chart into it. A chart is not one image with one meaning — it is a structure of many values, and the right alternative depends on how much of that structure a user needs. Match the treatment to the chart's complexity.
Decorative
A sparkline that only echoes a number already in the text. It adds no information of its own, so hide it with aria-hidden="true" and let the surrounding text carry the meaning.
One insight
A single chart drawn to make one point. A concise text summary that states the takeaway — the trend, the outlier — is often a sufficient alternative on its own.
Explorable data
Multiple series, many categories, values a user would want to read individually. The summary is not enough — you owe a data table with the numbers.
This mirrors the W3C's complex images model: a short text alternative that names the chart and its point, and a long alternative — the table or a prose description — that holds the full detail. The single most useful question to ask about any chart is the one a screen reader user is really asking: “Can I answer the same questions from your text that a sighted person can answer from your picture?” If the chart invites comparison of specific values, only a table lets them.
Do not read the whole dataset into an alt string. A summary describes the shape (“a steady rise from 40 to 210”); it does not recite every point. Reciting the data belongs in the table, where a screen reader user can navigate it cell by cell instead of enduring one unbroken sentence.
2. SVG, Canvas, or an Image?
How you render the chart decides how much accessibility work is even possible. The three technologies start from very different places, and the difference is entirely about whether the marks exist in the DOM.
| Technology | In the DOM? | What it means for accessibility |
|---|---|---|
Inline <svg> | Yes — every mark | The richest option. Label it as one image, keep axis text as real text, or make individual points focusable. Recommended for most charts. |
<img> (SVG or PNG source) | No — one opaque node | Simplest and robust. Real alt gives the summary; pair with a data table for detail. Nothing else is exposed. |
<canvas> | No — pixels only | The trap. The drawing has no accessible representation at all; you must supply fallback content and, in practice, a table. |
The practical guidance: prefer SVG (or a library that renders SVG) because the accessibility hooks come for free — it is text and shapes in the document, not a flat bitmap. Use an <img> when the chart is pre-rendered and you only need a summary plus a table. Treat <canvas> as opt-in extra work: it is fast for thousands of points, but everything a screen reader will ever know about the chart has to be provided separately, because the canvas itself is a black box.
<!-- Canvas exposes nothing it draws. Provide fallback + a real table. -->
<figure>
<canvas role="img" aria-label="Support tickets by month: a rise from 40 in January to 210 in June.">
<!-- Fallback content for anything that cannot use the canvas: -->
Support tickets rose from 40 in January to 210 in June.
</canvas>
<figcaption>Support tickets by month, 2026. <a href="#tickets-table">View data table</a></figcaption>
</figure>3. Labelling an SVG Chart
For a chart you are treating as a single described image — the common case — three things turn a jumble of shapes into one announced graphic: role="img" on the <svg>, an accessible name, and an accessible description.
<svg
role="img"
aria-labelledby="rev-title rev-desc"
viewBox="0 0 640 360"
width="640"
height="360"
>
<title id="rev-title">Monthly revenue, 2026</title>
<desc id="rev-desc">
Revenue rose from $12k in January to $47k in June, with a dip to $9k in April.
</desc>
<!-- bars, axes, labels... -->
</svg>role="img" does two jobs. It tells assistive technology to treat the SVG as a single image, and it collapses the internal shapes so a screen reader reads only your label — not every <path> and <rect> in turn. aria-labelledby points at the short name; the <desc> supplies the longer summary. For a one-line label you can use a plain aria-label instead.
<title> and <desc> alone are not reliably announced. They are the semantically correct elements, and you should keep them — but historically a bare <svg> with just a <title> is exposed inconsistently across browser and screen-reader pairings. The dependable pattern is to add role="img" and wire aria-labelledby (or aria-label) explicitly, then verify in your target screen readers. Never assume the native elements are read on their own.
Decorative SVG: hide it, and stop it stealing tab stops
When an SVG adds nothing — an icon, a purely ornamental flourish, a sparkline that repeats a nearby figure — hide it from assistive technology and keep its inner shapes out of the tab order. In some browsers, SVG elements (and the root <svg>in legacy engines) can become keyboard tab stops, so a decorative graphic can quietly add empty stops to every keyboard user's journey.
<svg aria-hidden="true" focusable="false" viewBox="0 0 100 24">
<!-- ornamental sparkline; the real number is in the text beside it -->
</svg>aria-hidden="true" removes it from the accessibility tree; focusable="false" keeps it out of the tab order. Use both on any SVG a keyboard or screen reader user has no reason to reach.
4. The Accessible Data Table: The Load-Bearing Alternative
For any chart with data worth reading individually, the data table is the alternative that actually carries the information. It is the one format that gives a screen reader user the same random access to values that a sighted user gets from scanning the chart — they can jump to a row, compare two cells, read a column header. Wrap the chart and its caption in a <figure>, and offer the table right there.
<figure>
<svg role="img" aria-labelledby="tickets-title" ...>
<title id="tickets-title">Support tickets by month, 2026</title>
<!-- bars... -->
</svg>
<figcaption>Support tickets by month, 2026.</figcaption>
<!-- The table can be visible, or revealed on demand like this: -->
<details>
<summary>Show data table</summary>
<table id="tickets-table">
<caption>Support tickets by month, 2026</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Tickets</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">January</th><td>40</td></tr>
<tr><th scope="row">February</th><td>72</td></tr>
<tr><th scope="row">March</th><td>115</td></tr>
<!-- ... -->
</tbody>
</table>
</details>
</figure>A few details make the table pull its weight. Use <th scope="col"> for column headers and <th scope="row">for the row label, so a screen reader announces “January, 40” rather than an unanchored “40” — that programmatic header-to-cell relationship is 1.3.1 Info and Relationships. The <details>/<summary> disclosure keeps the page tidy while leaving the table one keystroke away and needs no JavaScript. If you would rather keep the table always present but out of the visual layout, a sr-only table works too — but a visible, toggleable table often helps sighted users who just want the exact numbers, so consider showing it to everyone.
The chart and the table are two views of one dataset. Generate both from the same data so they can never disagree — a table that has drifted out of sync with the chart is worse than no table at all. For the full set of rules on building the table itself, the accessible tables demo walks through headers, captions, and responsive patterns.
5. Colour, Patterns, and Never Colour Alone
Charts lean on colour harder than almost any other interface, and that is exactly where they fail 1.4.1 Use of Color. If the only thing separating the “desktop” line from the “mobile” line is that one is blue and one is green, then a red-green colour-blind user — around one in twelve men — sees two identical lines, and so does anyone printing in greyscale. Colour can be a cue; it must never be the only cue.
Give every series a second, non-colour distinction:
- Label directly. Put each series' name at the end of its line or on its segment, so the reader never has to bounce between a legend and the chart to decode which colour is which.
- Vary the form. Different dash patterns for lines, different marker shapes (circle, square, triangle) for points, and textures or SVG
<pattern>fills for bars and areas — all survive greyscale. - Order meaningfully. In a stacked or grouped chart, a consistent order plus a direct label does more than colour ever could.
Contrast is a separate requirement from colour. Even a colour-blind-safe palette can fail 1.4.11 Non-text Contrast: a pale bar on a white background, or two adjacent segments only a shade apart, do not reach the required 3:1 for marks that carry meaning. Check chart marks against their neighbours and the background, and check label text against 1.4.3 with the contrast checker.
Picking the palette itself — a categorical set that stays distinguishable for colour-blind viewers and holds contrast in both light and dark mode — is its own craft. The accessible colour palettes guide covers building one, and the OKLCH & APCA guide covers the perceptual colour maths behind evenly-distinguishable series. The rule that ties it together: the safest chart is one that still reads in black and white.
6. Keep Labels as Real, Scalable Text
A chart is full of text — axis ticks, value labels, the legend, the title. How that text is rendered decides whether it survives zoom and reaches assistive technology. The failure mode is exporting the whole chart, labels and all, as a flat PNG.
Baking text into a raster image trips three criteria at once. It fails 1.4.5 Images of Text because the words are pixels, not text; it fails 1.4.10 Reflow and turns to mush when a low-vision user zooms to 400%; and small baked labels routinely fail 1.4.3 Contrast (Minimum) with no way to override the colour. Rendering the same labels as real text — SVG <text> elements, or HTML positioned over the chart — fixes all three: the text scales crisply, stays selectable and machine-readable, and can meet contrast because it is styled with CSS.
- Use vector output (SVG) so the whole chart, text included, scales without blurring when the page is zoomed.
- Keep axis and value labels as live
<text>, never as part of a background bitmap. - Meet 4.5:1 for normal label text (3:1 for large), and remember that a light grey axis label on white is a common, quiet failure.
- Do not rely on font sizes so small they force the user to zoom just to read a tick — respect zoom and reflow rather than fighting them.
7. Interactive Charts: Keyboard and Tooltips
The moment a chart responds to the mouse — hover tooltips, clickable points, drag-to-zoom — it takes on interaction requirements, and the most common failure is that all of the value lives behind a mouse. There are two honest ways to make an interactive chart operable for everyone, and the pragmatic one is usually right.
Expose the table (pragmatic)
Mouse users get the interactive chart; keyboard and screen reader users get the fully navigable data table as the route into the same values. Robust, cheap, and enough for most dashboards — you are not rebuilding a charting engine as a keyboard widget.
Focusable points (rich)
Make each data point focusable with tabindex, a role, and an aria-label announcing its value, and wire arrow keys to move between points like a grid. Worth the effort for genuine data-exploration tools.
Whichever path you take, two rules are non-negotiable for the tooltip that shows a point's value:
- Hover content must also appear on focus. If a value is revealed when the mouse hovers a point, the same value must appear when the point receives keyboard focus, it must stay visible while it is pointed at, and it must be dismissible (Escape) without moving focus — that is 1.4.13 Content on Hover or Focus.
- Announce the change. As focus moves from point to point, push the new value into a polite live region so a screen reader speaks “March, 115 tickets” without you having to move focus into a tooltip.
Managing where focus lands as the user enters, moves through, and leaves the chart is the same discipline as any composite widget — the focus management guide covers roving tabindex and restoration, and the keyboard accessibility guide covers the key handling. The golden rule for charts: never make a value available on hover only.
8. Charting Libraries: What They Do and Don't Give You
No library makes an inaccessible chart accessible for free, but they start you at very different points. Knowing what each one hands you — and what it leaves you owning — is the difference between a quick configuration and a surprise rebuild.
| Library | Renders | Built-in accessibility | What you still owe |
|---|---|---|---|
| Highcharts | SVG | A dedicated accessibility module: keyboard navigation, screen-reader descriptions, a data-table view, and audio sonification. | Enable the module and write meaningful chart and series descriptions. |
| ECharts | Canvas or SVG | An aria option that generates a text description of the chart. | Turn on the aria option, prefer the SVG renderer, and add a data table. |
| Chart.js | Canvas | None by default — canvas exposes nothing it draws. | Add an accessibility plugin or provide fallback content, an aria-label, and a table. |
| Recharts / Nivo / Victory | SVG (React) | A DOM you can annotate; minimal built-in ARIA that varies by release. | Add role="img" and a label, colours with a second cue, and a table. |
| D3 | SVG (you build) | Nothing — total control, zero defaults. | You own every role, label, colour cue, the keyboard path, and the table. |
Two practical takeaways. First, if accessibility matters and you have a choice, a library with a real accessibility module — Highcharts is the clearest example — saves you the most work, provided you actually enable it and write the descriptions. Second, whatever you pick, the responsibilities in this guide do not transfer to the vendor: the text alternative, the colour cues, the scalable labels, and the keyboard route are yours to verify. Treat the library's output as a starting point, then run the tests below.
9. Charts in React
React charting libraries such as Recharts render SVG, so the accessibility contract is the same as any SVG chart: name the graphic, and pair it with a reachable table. Wrap the chart in a <figure>, label the SVG as an image, and drive both the chart and the table from one array of data so they never drift. Use useId to tie the label and table together without hard-coded ids.
function TicketsChart({ data }) {
const id = useId()
const titleId = id + "-title"
const tableId = id + "-table"
const summary =
"Support tickets by month: a rise from " +
data[0].value + " in " + data[0].month + " to " +
data[data.length - 1].value + " in " + data[data.length - 1].month + "."
return (
<figure>
{/* role="img" + aria-label makes the whole SVG one described image.
Recharts renders the inner <svg>; wrap or configure it so the
accessible name lands on the chart's root svg element. */}
<div role="img" aria-label={summary}>
<BarChart data={data} /* ...Recharts config... */ />
</div>
<figcaption id={titleId}>Support tickets by month, 2026</figcaption>
<details>
<summary>Show data table</summary>
<table id={tableId}>
<caption>Support tickets by month, 2026</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Tickets</th>
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.month}>
<th scope="row">{row.month}</th>
<td>{row.value}</td>
</tr>
))}
</tbody>
</table>
</details>
</figure>
)
}The table is generated from the same datathe chart consumes, so it is always in sync — the single most important property of a chart's data-table alternative. A library will render the marks and often a hover tooltip, but it will not write your summary, guarantee your colours carry a second cue, or build the table; those stay your job. The same principles carry to the other frameworks — see the React, Vue, and Angular accessibility guides.
How to Test an Accessible Chart
A scanner can confirm an SVG has a label; it cannot tell you whether a blind user could actually read your data. These are the hands-on checks, and they take a couple of minutes each.
- Cover the picture. Ignore the visual and read only the text alternatives. Can you get the chart's finding from the summary, and the specific numbers from a table or description? If not, the data is trapped in the pixels (1.1.1).
- Run a screen reader over it. It should announce a meaningful description — not “image”, not silence, and not a stream of
pathelements. Use the commands in the NVDA and VoiceOver guides. - Turn off colour. Switch the display to greyscale and confirm you can still tell every series and category apart — the distinction is in labels, patterns, or shapes, not only in hue (1.4.1).
- Zoom to 400%. Labels should stay crisp and the layout should reflow without a horizontal scrollbar — proof the text is real and the output is vector (1.4.5, 1.4.10).
- Put the mouse away. If the chart is interactive, reach every value with the keyboard or through the table, confirm hover tooltips also appear on focus and can be dismissed, and listen for the value being announced as focus moves (1.4.13, 2.1.1).
- Measure contrast. Check the marks against their neighbours and the background for 3:1 (1.4.11) and the label text for 4.5:1 (1.4.3) with the contrast checker.
Layer axe-core on top for the mechanical checks — see automated vs manual testing — and scan the live page with the URL accessibility auditor to catch an unlabelled graphic or a missing table before it ships.
Common Chart Accessibility Mistakes & How to Fix Them
| Anti-pattern | Why it fails | The fix |
|---|---|---|
| The chart ships as an <img> with alt="chart" or an empty alt. | None of the data reaches assistive technology; a screen reader user learns only that an image exists (1.1.1). | Write alt text that states the chart's takeaway, and add an accessible data table for the underlying numbers when the chart has more than a trivial amount of data. |
| Series are told apart only by colour, decoded through a legend. | Colour-blind users and anyone in greyscale cannot map colours back to names, and matching a legend to the chart is extra cognitive load for everyone (1.4.1, 1.4.11). | Label series directly at the data, add patterns, dash styles, or marker shapes as a second cue, and keep 3:1 contrast between adjacent colours. |
| An inline <svg> chart has no role and no accessible name. | The screen reader either says nothing or reads out every <path> and <rect> as meaningless shapes (1.1.1, 4.1.2). | Add role="img" and an accessible name via aria-labelledby (pointing at <title>/<desc>) or aria-label, so the SVG is announced as one described image. |
| Axis labels and values are baked into a raster (PNG/JPG) image. | The text blurs when zoomed, is invisible to assistive technology, and often fails contrast at small sizes (1.4.5, 1.4.10, 1.4.3). | Render labels as real text — inline SVG <text> or HTML — so they stay crisp when zoomed, are exposed to AT, and can meet contrast requirements. |
| Each data point's value appears only in a tooltip on mouse hover. | Keyboard users, touch users, and screen reader users never reach the values, and the tooltip may not be dismissible (1.4.13, 2.1.1). | Show the same content on keyboard focus, make it dismissible, and expose the values through a reachable data table so hover is never the only source. |
| A <canvas> chart has no fallback content and no adjacent table. | Canvas paints pixels and produces no DOM for what it draws, so the chart is entirely absent from the accessibility tree (1.1.1). | Put fallback content (a description or table) between the <canvas> tags, add role="img" and aria-label for the summary, and provide a real data table for the detail. |
Accessible Chart Checklist
- Text alternative exists. Every chart has a real text equivalent — a summary — and anything non-trivial has an accessible data table for the numbers (1.1.1).
- Right technology. SVG or an
<img>you can label; any<canvas>chart has explicit fallback content and a table. - SVG is labelled.
role="img"plusaria-labelledbyoraria-label; decorative SVG usesaria-hidden="true"andfocusable="false". - Not colour alone. Every series carries a second cue — direct label, pattern, shape, or dash — and marks meet 3:1 contrast (1.4.1, 1.4.11).
- Real, scalable text. Labels are live text, not baked pixels; readable at 400% zoom, and label text meets 4.5:1 (1.4.5, 1.4.10, 1.4.3).
- Tooltips reachable. Hover content also appears on focus, is dismissible, and is never the only source of a value (1.4.13).
- Interactive means keyboard. Every data point is reachable by keyboard, or the table is exposed as the equivalent path (2.1.1).
- Changes announced. Values revealed on focus, and dynamic updates, go through a polite live region (4.1.3).
Work through the full WCAG 2.2 checklist to see charts in the context of every other requirement, and the accessible colour palettes guide for building a series palette that holds up.
Check Your Charts on a Live Page
Scan any page with our free axe-core-powered auditor to catch an unlabelled SVG, a chart image with no alt text, or a data table that never made it into the markup — then run the cover-the-picture and greyscale tests above for the failures no scanner can see.
Frequently Asked Questions
How do I make a chart accessible to screen readers?▾
Give the chart a text alternative that carries the same information the picture does, because a screen reader cannot see shapes and colours. For a simple chart with one clear takeaway, that can be a short text summary that states the trend — "Revenue rose from $12k in January to $47k in June, with a dip in April." For anything with more than a handful of data points, the real alternative is an accessible data table holding the numbers, placed near the chart or revealed by a toggle, because a table is the one format that gives a screen reader user the same random access to individual values that a sighted user gets from reading the chart. Then make sure the visual itself is not a black hole in the accessibility tree: an inline SVG needs role="img" and a label, a canvas chart needs fallback content, and a chart image needs real alt text. The picture is the enhancement; the text alternative is the part that has to work.
Do I need a data table for every chart?▾
No — match the treatment to the complexity. A sparkline that only reinforces a number already stated in text is decorative; hide it with aria-hidden="true" and let the text carry the meaning. A single chart with one insight can be served by a concise text summary that states the takeaway. But once a chart has multiple series, many categories, or values a user would reasonably want to read individually, a text summary cannot substitute for the numbers, and you owe an accessible data table. The table does not have to be visible by default — a "Show data table" toggle built from a details/summary element or a button is a common, tidy pattern — but it must be reachable. The test: could a screen reader user answer the same questions from your alternative that a sighted user can answer from the chart? If the chart invites comparison of specific values, only a table lets them.
Should I use an image, inline SVG, or canvas for an accessible chart?▾
All three can be made accessible, but they start from very different places. An <img> pointing at an SVG or PNG is the simplest robust option: give it real alt text (a summary, not "chart") and, for anything non-trivial, an accompanying data table — the image itself exposes nothing else to assistive technology. An inline <svg> is the richest: it lives in the DOM, so you can label it with role="img" and aria-labelledby, keep axis text as real selectable text, and even make individual data points focusable for an interactive chart. A <canvas> is the trap: it paints pixels and produces no accessible DOM for what it draws, so a canvas chart with nothing else is completely invisible to a screen reader. If you use canvas, you must supply fallback content inside the element and, in practice, an adjacent data table. For most charts, prefer SVG (or an SVG-rendering library) precisely because the accessibility hooks come built in.
How do I write good alt text or a text description for a chart?▾
Describe what the chart shows, not what it is. "Bar chart" tells a screen reader user nothing they could not guess; "Support tickets by month: a steady rise from 40 in January to 210 in June" tells them the actual finding. Lead with the takeaway — the trend, the outlier, the comparison the chart was drawn to make — because that is the insight a sighted reader extracts at a glance. Keep the short alternative short; do not try to read out every data point in an alt attribute, because that is unbearable to listen to and it is what the data table is for. So the pattern for a complex chart is two layers: a short text alternative (or aria-label) that gives the summary, and a longer alternative — the data table, or a paragraph below the figure — that holds the detail. The W3C's complex-images tutorial calls this the short-description-plus-long-description model, and it is the right mental model for charts.
Are SVG <title> and <desc> reliably announced by screen readers?▾
Not on their own, which surprises people. The SVG <title> and <desc> elements are the semantically correct way to name and describe a graphic, but historically screen readers and browsers have exposed them inconsistently — a bare <svg> with a <title> is often announced as nothing, or as a group of shapes. The reliable pattern is to add role="img" to the <svg> so the browser treats it as a single image, and then point aria-labelledby at the ids of your <title> and <desc> (or use a plain aria-label for the short version). role="img" also collapses the SVG's internal shapes so assistive technology reads only your label rather than announcing every <path> and <rect>, which is exactly what you want for a static chart. Keep the <title>/<desc> elements — they are good practice and help other tools — but do not rely on them being read without the ARIA wiring, and always verify in your target screen readers.
How do I make chart colours accessible and colour-blind friendly?▾
Two rules. First, never let colour be the only thing that distinguishes one series or category from another — that fails WCAG 1.4.1 Use of Color and leaves colour-blind users and anyone viewing in greyscale unable to tell your lines apart. Add a second cue: label lines directly at their end rather than relying on a legend, give each series a distinct pattern or texture, use different dash styles for lines and different marker shapes for points, and order stacked segments meaningfully. Second, give chart elements enough contrast — WCAG 1.4.11 Non-text Contrast asks for at least 3:1 between adjacent colours that carry meaning and against the background, so a pale bar on a white background fails even if its hue is distinct. Choosing a colour-blind-safe categorical palette helps, but it does not remove the need for a non-colour cue: the safest charts are readable in black and white. Our accessible colour palettes guide covers building such a palette.
How do I make an interactive chart keyboard accessible?▾
There are two honest paths, and the pragmatic one is often best. The pragmatic path is to expose the underlying data table as the keyboard-and-screen-reader route into the data: sighted mouse users get the interactive chart, everyone else gets a fully navigable table with the same values, and you have satisfied the requirement without rebuilding a charting engine as a keyboard widget. The richer path — worth it for genuine data-exploration tools — is to make each data point focusable, with tabindex, a role, and an aria-label announcing its value, and to wire arrow keys to move between points the way a grid does. Whichever you choose, two rules apply: any information shown in a hover tooltip must also appear on keyboard focus and be dismissible (WCAG 1.4.13 Content on Hover or Focus), and as focus moves between points, announce the focused value through a live region so a screen reader user hears it change (WCAG 4.1.3). Never make the values available on hover only.
What is the most accessible charting library?▾
Among mainstream libraries, Highcharts has the most complete built-in accessibility: an accessibility module that adds keyboard navigation, screen-reader descriptions of the chart and each series, a data-table view, and even audio sonification — you enable it and write meaningful series descriptions. ECharts has an aria option that generates a text description and can render as SVG. Chart.js renders to canvas, so it exposes nothing by default; you need an accessibility plugin or you must provide your own fallback table and an aria-label. D3 gives you total control and zero defaults — you own every role, label, and the data table. React libraries that output SVG, such as Recharts, Nivo, and Victory, give you a DOM you can annotate, but their built-in ARIA is minimal, so you still add role="img", a label, and a table yourself. The honest summary: either choose a library with a real accessibility module and configure it, or accept that you are responsible for the text alternative, the colours, and the keyboard path. No library makes an inaccessible chart accessible for free.
Essential Accessibility Resources
Comprehensive tools, checklists, and guides to help you create inclusive digital experiences