Accessible Histograms and Distributions

Permalink to "Accessible Histograms and Distributions"

Distribution charts — histograms of response times, box plots of order values, density curves of scores — answer questions about shape: Where is most of the data? Is it skewed? Are there outliers? Sighted users read shape at a glance. A screen reader user hearing twenty bars one by one must reconstruct it mentally, which is slow and error-prone. Accessible distribution charts lead with the shape in words and statistics, then offer the bins for those who want detail.

This guide belongs to chart type-specific patterns.

Spec reference

Permalink to "Spec reference"
  • SC 1.1.1 Non-text Content (A) — the text alternative should convey what the chart shows — here, the distribution’s shape and key statistics.
  • SC 1.3.1 Info and Relationships (A) — bins as a table with range and count headers.
  • SC 2.1.1 Keyboard (A) — interactive bars reachable by keyboard.
  • SC 1.4.11 Non-text Contrast (AA) — bars and reference lines (median, thresholds) at 3:1.
A response-time histogram Histogram of API response times in 100 ms bins: most requests fall between 100 and 300 ms, with a long tail and a small cluster above 1 second. A response-time histogram0–100 ms120 requests100–200 ms840 — the peak200–300 ms610300–400 ms220400–500 ms90over 1 s60 — second cluster worth investigating
Right-skewed with a second small peak above one second — exactly what the summary should say.

When to use — and when not to

Permalink to "When to use — and when not to"

Use histograms and box plots to show distributions, and always lead with a description: “Most requests take 100–300 ms (median 180 ms). The distribution is right-skewed, with 3% of requests over 1 second.” For comparing distributions across groups, box plots with a statistics table are often clearer than overlaid histograms.

Do not present a histogram without bin ranges (bars labelled 1, 2, 3…), and do not rely on density curves alone — they have no countable bars at all.

The misapplication: an alt text of “Histogram of response times”. It names the chart and conveys none of what it shows.

Naming the chart versus describing the distribution Comparison: an alt text that names the histogram conveys nothing; a description gives centre, spread, shape and outliers, with a bin table for detail. Naming the chart versus describing the distribution✗ "Histogram of response times"No centreNo spreadNo shapeNo outliersNo detail✓ Distribution describedMedian 180 msMiddle half between 120 and 260 msRight-skewed with a long tail3% above 1 second, a separate clusterBin table with counts and shares

Annotated code example

Permalink to "Annotated code example"
<figure aria-labelledby="h-t" aria-describedby="h-d">
  <h3 id="h-t">API response times, last 24 hours</h3>
  <div id="h-d">
    <p>2,000 requests. Median 180 ms; middle half between 120 and 260 ms.</p>
    <p>Right-skewed: most requests take 100–300 ms, with a long tail. A second small cluster (60 requests, 3%) took over 1 second.</p>
  </div>
  <svg viewBox="0 0 20 20" role="group" aria-label="Histogram, 100 millisecond bins">
    <!-- Bars with range and count (SC 1.1.1, 2.1.1) -->
    <rect tabindex="0" role="img" aria-label="100 to 200 milliseconds: 840 requests, 42 percent" x="2" y="4" width="3" height="14"/>
  </svg>
  <table><caption>Response time bins (100 ms wide)</caption>
    <thead><tr><th scope="col">Range</th><th scope="col">Requests</th><th scope="col">Share</th></tr></thead>
    …
  </table>
</figure>
// Compute statistics and shape words from data, not by hand
const q = quantiles(values, [0.25, 0.5, 0.75]);
const skew = (mean(values) - q[1]) / std(values);
const shape = skew > 0.3 ? 'right-skewed' : skew < -0.3 ? 'left-skewed' : 'roughly symmetric';
const outliers = values.filter(v => v > q[2] + 1.5 * (q[2] - q[0]));

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Event Expected announcement AT deviations
Reach figure Title, then statistics and shape —
Tab / arrows through bars “100 to 200 milliseconds: 840 requests, 42 percent” —
Box plot element focus “Order value: median 48, quartiles 31 to 76, range 4 to 410, 12 outliers” —
Table Range, count, share —
Distribution charts and their text equivalents Matrix of chart types — histogram, box plot, density curve, violin plot — against the statistics to state, a table form and a keyboard model. Distribution charts and their text equivalentsStatistics to stateTable formKeyboard modelHistogramMedian, spread, shapeBins with countsBar by barBox plotFive-number summary, outliersOne row per groupGroup by groupDensity curvePeaks and spreadBinned approximationNot naturalViolin plotFive numbers + peaksOne row per groupGroup by group
Each chart type has a natural text form; use it.

Integration context

Permalink to "Integration context"

Bar-by-bar navigation is the same as adding keyboard navigation to canvas bar charts. Writing the description follows writing chart summaries and key takeaways. Sonification can convey shape effectively too — see sonifying time series charts, whose technique applies to bins.

Gotchas

Permalink to "Gotchas"

Bin width changes shape. A description computed from 10 ms bins differs from one computed from 500 ms bins. Compute shape words from the data, not from bins.

Log scales. If the axis is logarithmic, say so in the description and the axis label; it changes how everyone reads the chart.

Open-ended bins. “Over 1 s” must be named as such, not as “1000–1100 ms”.

Common mistakes

Permalink to "Common mistakes"
  • Alt text that names the chart but not the distribution.
  • Bars labelled with indices instead of ranges.
  • Hand-written statistics that go stale.
  • Density curves with no table.

Framework notes

Permalink to "Framework notes"

Vega-Lite can generate histograms with bin: true and compute statistics with transforms; render a description from the same transforms. Observable Plot Plot.rectY(data, Plot.binX()) produces bins you can reuse for the table. Highcharts histogram series work with the accessibility module; add a custom description with statistics. For box plots, most libraries expose quartiles — use them for both the drawing and the text.

// Observable Plot: reuse binning for the table
const bins = d3.bin().thresholds(20)(values).map(b => ({ range: `${b.x0}–${b.x1} ms`, count: b.length }));

Worked example

Permalink to "Worked example"

A data science team published model latency histograms in a weekly report. Each chart had the alt text “Latency histogram”. A blind ML engineer on the team relied on colleagues to interpret them. The team added generated descriptions — “Median 42 ms, 95th percentile 190 ms, right-skewed; a new cluster near 600 ms appeared this week (2% of requests)” — and bin tables under each chart. The new cluster turned out to be a regression that everyone, not only the blind engineer, had missed in the pictures.

Writing the summary

Permalink to "Writing the summary"

Distribution summaries follow a fixed order that listeners can rely on: how many observations, where the middle is, how spread out they are, what shape they form, and what is unusual. “2,000 requests. Median 180 ms; half took between 120 and 260 ms. Most are fast, with a long tail of slower requests. A separate cluster of 60 requests took over a second.” For comparisons between groups, give the medians side by side and the main difference: “Mobile requests are slower: median 240 ms versus 150 ms on desktop.”

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Provide distribution chart components that compute statistics and shape descriptions, render a bin or summary table, and accept a domain unit formatter. Product teams should never write “Histogram of X” as the only text.

FAQ

Permalink to "FAQ"
How detailed should the description be?

Two or three sentences: centre, spread, shape and anything unusual. The table carries the rest.

Should box plots announce every outlier?

Count them and name the extremes; list all of them in the table if they matter.

Is "right-skewed" too technical?

For general audiences, say “most values are low, with a long tail of higher ones”. For analysts, both are fine.

How do I compare two distributions accessibly?

A table with one row per group and the five-number summary, plus a sentence on the main difference.

Should bars be individually focusable?

With one tab stop and arrow keys, yes; for many bins, the table is usually more efficient.

Can sonification help?

Yes. Playing bin heights as pitches conveys shape quickly; offer it as an option alongside the description.

How do I make a box plot keyboard accessible?

One focus stop per group, announcing the five-number summary and outlier count; Enter can open a list of outliers. Individual whiskers and quartile lines do not need separate stops.

What about cumulative distribution charts?

Describe percentiles in words (“90% of requests finish within 400 ms”) and provide a percentile table; they map naturally to the chart’s purpose.

Permalink to "Related"

← Back to Chart Type-Specific Patterns