How charts accessibility works
All charts created with the @semcore/d3-chart
package are available for screen readers such as JAWS, NVDA, Apple VoiceOver, Chrome Vox and others. With keyboard or braille display navigation users may reach a block that is hidden for other users. That block contains anchor links for faster navigation, an autogenerated summary of the chart, and a plain table containing all the data used for visualization.
The data table may not look pretty but it is fully accessible for screen readers.
Colorblind and low vision solution
Using patterns in charts instead of just colors makes them easier to understand for people who are color-blind or have low vision. Some colors can be hard to differentiate for these users, even if selected thoughtfully and with different contrasts. This can make it hard to read charts. Adding patterns helps everyone see the differences between data sets clearly.
Our charts feature properties that enable pattern fills for charts with areas to fill, display symbols instead of dots, and convert solid lines to dashed lines for charts without areas to fill:
patterns
property forPlot
patterns
propertyChartLegend
(works only withshape={'Checkbox'}
)
Refer to our live examples, for more details on the feature usage.
Examples of accessible charts
Example summary (autogenerated):
Chart represents 1 time series of Temperature: weakly growing from 15.5 to 16.5, also strongly growing from January to July and strongly declining from July to December.
Temperature is represented from January to December.
Chart represents 1 time series of Temperature: weakly growing from 15.5 to 16.5, also strongly growing from January to July and strongly declining from July to December.
Temperature is represented from January to December.
Example summary (autogenerated):
Chart represents 3 clusters of sizes from 1 to 66 of Height: significantly big cluster of 66 size around cross of 68.348 Weight and 174.076 Height, significantly small cluster of 1 size around cross of 78 Weight and 153 Height, and significantly small cluster of 1 size around cross of 99 Weight and 199 Height.
Weight represented from 50 to 99 and Height represented from 148 to 199
Chart represents 3 clusters of sizes from 1 to 66 of Height: significantly big cluster of 66 size around cross of 68.348 Weight and 174.076 Height, significantly small cluster of 1 size around cross of 78 Weight and 153 Height, and significantly small cluster of 1 size around cross of 99 Weight and 199 Height.
Weight represented from 50 to 99 and Height represented from 148 to 199
Example summary (autogenerated):
Chart represents 3 groups each containing 3 values of Genre preferences in survey: group adults contains thriller of value 80, fiction of value 28, and romance of value 20, group elderly contains romance of value 70, fiction of value 24, and thriller of value 18, and group teenagers contains fiction of value 63, thriller of value 25, and romance of value 19.
Chart represents 3 groups each containing 3 values of Genre preferences in survey: group adults contains thriller of value 80, fiction of value 28, and romance of value 20, group elderly contains romance of value 70, fiction of value 24, and thriller of value 18, and group teenagers contains fiction of value 63, thriller of value 25, and romance of value 19.
Example summary (autogenerated):
Chart represents 3 values of Survived passengers: Class 3 of value 218, Class 1 of value 107, and Class 2 of value 93.
Chart represents 3 values of Survived passengers: Class 3 of value 218, Class 1 of value 107, and Class 2 of value 93.
A pie chart representing the same data will have the same summary.
How to make your charts more accessible
Define all properties
To make sure your users get a meaningful data summary, provide a locale
property (for example, en
, zh
or es
) for the <Plot />
component of the chart. This property defines the language of the accessibility card and the language the data summary will be generated in. Another essential property is label
. This property should briefly describe what is represented on the chart, like "Gold stocks" or "Subscribers overview".
Best practices
- Make sure to add both the
<XAxis.Title>...<XAxis.Title>
and the<YAxis.Title>...</YAxis.Title>
component. These components help the summarizer generate a better chart summary. - Check that the generated summary describes your chart correctly. To do that, press Tab to see the text and review it.
- Make sure that all the information in the tooltips is also provided in the
data
property of<Plot />
. - If you are redefining children rendering of the
<XAxis.Ticks>
or<YAxis.Ticks>
component, return both thechildren
and thevalue
properties. More on this below. - Only use chart interactivity for visual, unimportant effects.
- If your chart is highly customized, the summary generator may struggle with the summary. To fix that, either tweak the data summarizer configuration or add your own summary manually. More on this below.
Edge cases
Redefining children rendering of ticks
If you are redefining children rendering of <XAxis.Ticks>
or <YAxis.Ticks>
beside children
return value
property in render function. So value
would be used to describe a tick in the generated summary. Note that render function may be called more times than ticks displayed because some ticks making sense for summary generation may make no sense for visualization.
<XAxis>
<XAxis.Title>Year</XAxis.Title>
<XAxis.Ticks ticks={xScale.ticks(6)}>
{({ value }) => ({
value: new Date(value),
children: formatDate(value),
})}
</XAxis.Ticks>
</XAxis>
<XAxis>
<XAxis.Title>Year</XAxis.Title>
<XAxis.Ticks ticks={xScale.ticks(6)}>
{({ value }) => ({
value: new Date(value),
children: formatDate(value),
})}
</XAxis.Ticks>
</XAxis>
Tweak data summarizer configuration
If the chart summary looks similar to what you expect to tell users but not good enough–especially with your data (for example, your data is too variable and the summarizer seems to be too sensitive) – you can tweak the summarizer's configuration.
PlotSummarizerConfig
Name | Type | Description |
---|---|---|
disable | boolean | Totally disable automatic data summarization |
override | string | Disable automatic data summarization and use provided text |
trendTangens | {static?: number; weak?: number; medium?: number; strong?: number} | Tuning up time series trends analyzing. Represents angle tangens of time series change of different strength |
movingAverage | {longSize?: number; shortSize?: number; notableDiff?: number} | Tuning up time series local trends detection based on moving averages |
dataType | "time-series" | "points-cloud" | "values-set" | The way data to be interpreted as. By default is based on used used Plot elements and data structure. |
clustersGridSize | number | Grid size to split data of point clouds into clusters. |
maxListSymbols | number | Limits some output lists in chart alt text |
datesWithTime | boolean | Always add time to dates in chart alt text * |
clustersLimit | number | Described clusters count before text "and X more" |
valuesLimit | number | Described values count before text "and X more" |
groupsLimit | number | Described grouped value groups count before text "and X more" |
additionalFields | string[] | Additional field for extra text description to the data * |
titlesFormatter | (key: string | number | null) => string | undefined | Allows to format titles (e.g. axes) in alt text and data table |
valuesFormatter | (value: unknown, key: string | number | null) => string | undefined | Allows to format values in alt text and data table |
Add summary manually
If the automatically generated summary isn’t suitable for the data provided to the <Plot />
component, simply disable the summarizer and provide your own text.
<Plot
scale={[xScale, yScale]}
width={width}
height={height}
data={data}
a11yAltTextConfig={{
override:
"This chart represents the sales of our great unicorn startup. We haven't sold anything.",
}}
::: tip
:::
...
<Plot
scale={[xScale, yScale]}
width={width}
height={height}
data={data}
a11yAltTextConfig={{
override:
"This chart represents the sales of our great unicorn startup. We haven't sold anything.",
}}
::: tip
:::
...
Data summarizer architecture
Data summarization is performed on the client side in linear time, linear memory and split into three phases:
- Catching data structure hints from visualization components (such as
<Line />
or<Bar />
). - Extracting data insights into abstract insights blocks.
- Transforming the abstract insights blocks into human-readable text.
How to contribute to the chart accessibility module
If you are interested in expanding the charts accessibility module for your needs or in ejecting the accessibility module into a separated package, feel free to open a pull request. We will react to it sooner.