Show the code
import matplotlib.pyplot as plt
# Just import maidr package: plt.show() now renders accessible output
import maidr A pie chart and a word cloud have something in common that makes them hard for a screen reader: their data is drawn as angle or as glyph size and written down nowhere on the page. py-maidr reads both as a flat row of labeled values, so the Left and Right arrows walk the slices or terms and each stop is announced by its label, its value and, for a pie, the share of the whole it works out to. On a pie, Right walks clockwise round the dial and Left walks back.
PIE is in the stable set. WORD_CLOUD is experimental and may change without a deprecation period: see Plot Type Stability.
Every example on this page needs only the import below. The plot cells repeat it so each one can be copied on its own.
A pie is one flat row of slices, so navigation runs left and right across them and there is no second dimension to move through. Right steps to the next slice clockwise and Left to the previous one, so the highlight travels round the dial the way a clock hand does. Each slice is announced by its label, its value, and the share of the whole that value works out to. The percentage is derived from the values at render time rather than authored, so it can never disagree with the numbers it is derived from.
ax.pie() normalizes what it is given — it draws x / sum(x), and each wedge keeps only its angles — so the counts below survive as counts because maidr reads them off the call rather than off the drawn wedges.
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load the tips dataset
tips = sns.load_dataset("tips")
# Count the tips recorded on each day of the week
day_counts = tips["day"].value_counts()
fig, ax = plt.subplots(figsize=(8, 8))
# autopct draws the percentages onto the chart for sighted readers; maidr
# derives its own from the values, so the two cannot drift apart.
ax.pie(
list(day_counts.values),
labels=list(day_counts.index),
autopct="%1.1f%%",
startangle=90,
)
ax.set_title("Share of Tips by Day")
# Name the two dimensions of a slice, so it is read out as
# "Day: Sat, Number of tips: 87" rather than "X: Sat, Y: 87".
ax.set_xlabel("Day")
ax.set_ylabel("Number of tips")
plt.show() A pie has no x or y scale, so its two axis labels name the dimensions of a slice instead: xlabel says what the slice labels mean and ylabel says what the slice values measure. Leave them unset and maidr falls back to “Category” and “Value”, which at least read as English where “X” and “Y” would not.
Negative sizes are rejected — matplotlib refuses to draw a wedge with no area, so ax.pie() raises before maidr sees the call at all; maidr repeats the check for anyone building a PiePlot directly.
The slices are walked clockwise, whichever way matplotlib drew them. ax.pie() lays its wedges out counterclockwise unless counterclock=False, so in the chart above the first slice, Sat, runs from 12 o’clock down the left-hand side and Sun sits below it. Walking that draw order with Right would send the highlight the wrong way round the dial — and the audio, which pans each slice to where it sits, would put Sat on the right. maidr therefore reads a counterclockwise pie in reverse: Fri, then Thur, Sun and Sat, which is the clockwise order from 12 o’clock and the one the description’s data table lists. A pie drawn with counterclock=False is already clockwise and is read as drawn. Options that only move the wedges around (startangle, explode, shadow) do not change the direction and so leave the reading alone. Every slice’s highlight, value and share stay on the wedge they describe either way.
Where the dial starts is passed along too. maidr pans each slice to where it sits and, on p, names its clock position — both measured from where the first slice begins. startangle is emitted for that, converted from matplotlib’s degrees counterclockwise from 3 o’clock to the renderer’s degrees clockwise from 12: the chart above sets startangle=90, so its walk begins at the top, and a pie left at matplotlib’s default begins at 3 o’clock. A renderer older than the one that reads the value ignores it and places every pie from the top.
This is one of the experimental plot types. It has not been through a user study, and it may change without a deprecation period. See Plot type stability.
A word cloud is the extreme case of a chart that carries real data while being readable only by eye: each term’s weight is drawn as glyph size and written down nowhere on the page. Structurally it is a categorical label and a magnitude, so maidr reads it as a term and its number.
wordcloud is an optional dependency — pip install maidr[wordcloud].
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import maidr
mentions = {
"accessibility": 412, "sonification": 300, "braille": 250,
"screenreader": 190, "keyboard": 155, "contrast": 120,
"captions": 95, "semantics": 70,
}
# Pass the `WordCloud` object itself -- see the note below on `to_array()`.
cloud = WordCloud(
width=800, height=400, background_color="white",
max_words=8, random_state=42,
).generate_from_frequencies(mentions)
fig, ax = plt.subplots(figsize=(8, 4))
ax.set_title("What the accessibility reports talk about")
ax.imshow(cloud)
ax.set_axis_off()
plt.show() A cloud has no x or y scale — the glyph positions are packing, not data — so maidr names the two axes after what a point holds rather than where it sits. No labels were set above, so the defaults fire and a term reads as “Term: braille, Relative frequency: 0.607”. The generic “X”/“Y” the base class would otherwise give says nothing about a word.
The weights are relative, and that y axis name says so. WordCloud divides every frequency by the largest one in generate_from_frequencies and keeps only the ratio — the raw counts are on no attribute of the object. So the counts above are announced as 1.0, 0.728, 0.607, and the axis is called “Relative frequency” rather than “Occurrences”. Naming it after counts would hand a reader “accessibility, 1.0” for a term that occurred 412 times.
That is also what the chart draws: glyph size is proportional to the ratio, so a reader hearing 1.0 and 0.728 hears what a sighted reader sees. (The R binding is handed the raw counts, so it can honestly say “Occurrences” — same chart, two different honest readings.)
Show the object, not the picture. wc.to_array() and wc.to_image() hand imshow a plain RGB array, and the terms are not in it. A cloud displayed that way stays a picture and is not read — which is the honest answer, since the data never reached maidr.
Name the axes yourself if the defaults are too generic. An authored label always wins, so a cloud over survey topics can say so:
which reads as “Topic: braille, Share of mentions: 0.607”. Keep the y name honest about being a ratio — “Occurrences” would be wrong for the same reason the default avoids it.
The highlight is an outline round the word. imshow rasterises the whole cloud into a single image, so there is no element per word to light up. maidr measures where WordCloud drew each word and puts a box over it that the page never shows; as you move with the arrow keys, the word being read is outlined in your highlight color and stays legible inside the outline. Nothing is added to a cloud saved as a picture. If a word did not fit into the cloud at all, there is nothing to outline for it, and the cloud is read without a highlight rather than with outlines on the wrong words.