Who this is for
Data Visualization Engineers who ship dashboards, charts, and interactive reports and want users with screen readers to get the same insights without friction.
Prerequisites
- Basic HTML knowledge (elements, attributes).
- Familiarity with your charting approach (SVG, Canvas, or DOM-based).
- Comfort with reading the accessibility tree conceptually.
Why this matters
Real tasks you will face:
- Giving a chart an accessible name so a screen reader can announce what it is.
- Explaining the takeaway (trend, max/min) without forcing users to parse every data point.
- Labeling interactive elements (filters, toggles, legend items) so they are navigable and understandable by keyboard and screen readers.
- Summarizing dynamic changes (e.g., filter applied) so screen reader users are informed.
Concept explained simply
Screen readers need two things from you: a clear name and a clear description.
- Accessible name: what this thing is. Provide via aria-label or aria-labelledby.
- Accessible description: extra context. Provide via aria-describedby.
For complex visuals, group related elements and label the group. Use roles to set expectations (e.g., role="img" for non-interactive graphics).
Mental model
Think of every visual as a card with two lines:
- Title (name): announced first. Use aria-label or a heading + aria-labelledby.
- Footnote (description): the gist or summary. Use aria-describedby with a short text summary.
Tip: When to use which attribute
- aria-label: when you need to provide a name directly in code.
- aria-labelledby: when you already have visible text on the page that should become the name.
- aria-describedby: for supporting details, patterns, caveats, or data summaries.
Key patterns you will reuse
- Non-interactive chart block: role="img" + name + description.
- Interactive controls (buttons, checkboxes): each must have a name; group them with fieldset/legend or aria-labelledby.
- Groups of elements (series, bars): wrap in a container with role="group" and its own label.
- Dynamic updates: announce changes by updating the description text referenced by aria-describedby (keep changes concise).
Worked examples
Example 1: Static KPI sparkline
<figure>
<div id="kpi-title">Signups — last 7 days</div>
<div id="kpi-desc">Trend rising: 120 on day 1 to 210 on day 7. Peak 210 on day 7.</div>
<svg role="img" aria-labelledby="kpi-title" aria-describedby="kpi-desc" width="320" height="60">
<!-- sparkline path here -->
</svg>
</figure>Why it works: The SVG is an announced image with a clear title and useful summary.
Example 2: Bar chart with labeled groups
<section>
<h3 id="rev-title">Quarterly Revenue by Product</h3>
<p id="rev-desc">Q2 leads with $2.1M. Product Alpha outperforms others in Q2.</p>
<div role="img" aria-labelledby="rev-title" aria-describedby="rev-desc">
<div role="group" aria-label="Product Alpha bars">
<div role="img" aria-label="Q1: $1.2M"></div>
<div role="img" aria-label="Q2: $2.1M"></div>
<div role="img" aria-label="Q3: $1.7M"></div>
<div role="img" aria-label="Q4: $1.9M"></div>
</div>
<div role="group" aria-label="Product Beta bars">
<div role="img" aria-label="Q1: $0.9M"></div>
<div role="img" aria-label="Q2: $1.6M"></div>
<div role="img" aria-label="Q3: $1.4M"></div>
<div role="img" aria-label="Q4: $1.5M"></div>
</div>
</div>
</section>Why it works: The chart has a single announced name and description. Series are grouped for quicker navigation.
Example 3: Interactive legend buttons
<div role="group" aria-labelledby="legend-title">
<div id="legend-title">Toggle series</div>
<button aria-pressed="true" aria-label="Show Product Alpha series">Alpha</button>
<button aria-pressed="false" aria-label="Show Product Beta series">Beta</button>
</div>Why it works: Each button has an accessible name and communicates state via aria-pressed.
Step-by-step: make a chart screen-reader friendly
- Add a visible title or provide an aria-label on the chart container.
- Write a one-sentence summary (trend, peak, dip); point aria-describedby to it.
- For series, wrap related items in role="group" with an aria-label.
- Ensure all interactive elements are focusable and named.
- Keyboard-test: Tab through; ensure announcements make sense.
Quick keyboard-test routine
- Tab: focus moves logically through controls.
- Space/Enter: toggles activate and announce updated state.
- Escape: dismiss modals/menus if present.
Common mistakes and self-check
- Mistake: Using both aria-label and aria-labelledby on the same element with different texts. Result: confusing or duplicated names. Fix: pick one source of truth.
- Mistake: role="img" without a name. Fix: add aria-label or aria-labelledby.
- Mistake: aria-hidden="true" on focusable elements. Fix: don’t hide focusable controls.
- Mistake: Long, data-dense descriptions. Fix: summarize the insight; move raw data to a collapsible details element if needed.
- Mistake: Describing color only. Fix: include text equivalents (e.g., “Alpha series” not “blue bars”).
Self-check script
- Turn on a screen reader. Navigate to the chart. Do you hear a short, clear title?
- Is there a concise summary that states the key trend?
- Do legend items and filters announce their role and state?
- Can you reach and operate everything with just the keyboard?
Exercises
Do these now. They mirror the practice tasks below. You can compare with solutions.
- Exercise 1: Give a bar chart an accessible name and description, and label each bar (see Exercise 1 in the Practice section below).
- Exercise 2: Fix ARIA in a flawed snippet so the chart and controls are announced correctly (see Exercise 2 below).
Practice tasks
Exercise 1 — Name, describe, and label
Goal: Mark up a static monthly sales bar chart so a screen reader user understands the chart, the takeaway, and each bar.
Instructions
- Create a container for the chart with role="img".
- Provide a title as the accessible name.
- Add a concise summary as the accessible description.
- Label each bar with month and value.
Exercise 2 — Fix it
Goal: Correct misused ARIA attributes in a chart with toggles.
Flawed snippet
<div role="img">
<h3 id="t">Daily Active Users</h3>
<!-- Missing both name and description references -->
<button aria-label="">Alpha</button>
<button aria-pressed="yes">Beta</button>
</div>What to achieve
- The chart announces its title and a short summary.
- Buttons have clear names and correct pressed state.
Build checklist
- Chart has a programmatic name: aria-label or aria-labelledby.
- Chart has a one-sentence description via aria-describedby.
- Interactive controls are focusable, named, and expose state (e.g., aria-pressed).
- Groups (series) use role="group" with labels.
- No duplicated announcements from overlapping labels.
Practical projects
- Retrofit one existing dashboard chart with proper name, description, and labeled legend toggles.
- Create a small component library snippet: AccessibleChart wrapper that enforces aria-labelledby and aria-describedby.
- Add descriptive updates when filters change by updating text referenced by aria-describedby.
Learning path
- Start here: Names and descriptions (this lesson).
- Next: Keyboard interactions for chart controls (focus order, roving tabindex).
- Then: Announcing live updates responsibly (aria-live minimal use).
- Advanced: Data tables as semantic fallbacks for complex charts.
Mini challenge
In 10 minutes, take one chart and implement: aria-labelledby for the title, aria-describedby with a 15–25 word summary, and labels for any interactive controls.
Next steps
- Apply the checklist to all charts in one dashboard page.
- Pair-test with a colleague using a screen reader and keyboard only.
- Document your team’s standard for chart naming, describing, and grouping.
Quick Test
This test is available to everyone; only logged-in users get saved progress.