Chartjs Accessibility
by sjnims
This skill should be used when the user asks "Chart.js accessibility", "accessible charts", "WCAG Chart.js", "Chart.js ARIA labels", "Chart.js screen reader", "colorblind-safe charts", "Chart.js keyboard navigation", "Chart.js reduced motion", "prefers-reduced-motion Chart.js", "Chart.js alt text", "Chart.js color contrast", "accessible data visualization", "Chart.js focus management", "Chart.js fallback content", "aria-label canvas", or needs help making Chart.js visualizations accessible and W
Skill Details
Repository Files
4 files in this skill directory
name: chartjs-accessibility description: This skill should be used when the user asks "Chart.js accessibility", "accessible charts", "WCAG Chart.js", "Chart.js ARIA labels", "Chart.js screen reader", "colorblind-safe charts", "Chart.js keyboard navigation", "Chart.js reduced motion", "prefers-reduced-motion Chart.js", "Chart.js alt text", "Chart.js color contrast", "accessible data visualization", "Chart.js focus management", "Chart.js fallback content", "aria-label canvas", or needs help making Chart.js visualizations accessible and WCAG-compliant in v4.5.1.
Chart.js Accessibility (v4.5.1)
Making charts accessible ensures all users, including those with disabilities, can understand your data visualizations. This guide covers WCAG compliance, screen reader support, color accessibility, keyboard navigation, and motion sensitivity.
Canvas Accessibility Basics
Canvas elements are inherently inaccessible to screen readers. Add proper ARIA attributes and fallback content.
ARIA Labels and Roles
<canvas
id="myChart"
role="img"
aria-label="Bar chart showing quarterly sales: Q1 $12,000, Q2 $19,000, Q3 $15,000, Q4 $25,000"
></canvas>
Fallback Content
Provide content inside the canvas tag for screen readers and browsers without canvas support:
<canvas id="myChart" role="img" aria-label="Sales comparison chart">
<p>Quarterly sales data: Q1 had $12,000, Q2 had $19,000, Q3 had $15,000, Q4 had $25,000. Q4 showed the highest performance.</p>
</canvas>
Using aria-describedby
Link to a detailed data summary:
<canvas
id="myChart"
role="img"
aria-label="Monthly revenue chart"
aria-describedby="chart-description"
></canvas>
<div id="chart-description" class="visually-hidden">
Revenue grew steadily from January ($10K) to December ($45K),
with a notable spike in November during the holiday season.
</div>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
Color Accessibility
Ensure charts are usable by people with color vision deficiencies.
Colorblind-Safe Palettes
The Okabe-Ito palette is optimized for all types of color blindness:
const okabe_ito = [
'#E69F00', // Orange
'#56B4E9', // Sky Blue
'#009E73', // Bluish Green
'#F0E442', // Yellow
'#0072B2', // Blue
'#D55E00', // Vermilion
'#CC79A7', // Reddish Purple
'#000000' // Black
];
new Chart(ctx, {
type: 'bar',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [12, 19, 15, 25],
backgroundColor: okabe_ito.slice(0, 4)
}]
}
});
Pattern Fills with chartjs-plugin-patternomaly
Add patterns to distinguish data without relying on color:
<script src="https://cdn.jsdelivr.net/npm/patternomaly@1.3.2/dist/patternomaly.min.js"></script>
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
data: [12, 19, 15, 25],
backgroundColor: [
pattern.draw('diagonal', '#E69F00'),
pattern.draw('dot', '#56B4E9'),
pattern.draw('zigzag', '#009E73'),
pattern.draw('cross', '#0072B2')
]
}]
}
});
Available patterns: plus, cross, dash, cross-dash, dot, dot-dash, disc, ring, line, line-vertical, weave, zigzag, zigzag-vertical, diagonal, diagonal-right-left, square, box, triangle, triangle-inverted, diamond, diamond-box.
Contrast Requirements
WCAG AA requires:
| Element | Minimum Ratio |
|---|---|
| Text (normal) | 4.5:1 |
| Text (large 18px+) | 3:1 |
| Graphical objects | 3:1 |
| UI components | 3:1 |
Ensure data elements have sufficient contrast against background:
options: {
plugins: {
legend: {
labels: {
color: '#1a1a1a' // High contrast text
}
}
},
scales: {
x: {
ticks: { color: '#1a1a1a' },
grid: { color: '#666666' }
},
y: {
ticks: { color: '#1a1a1a' },
grid: { color: '#666666' }
}
}
}
Keyboard Navigation
Enable keyboard access for interactive chart elements.
Focus Management for Tooltips
const chart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
plugins: {
tooltip: {
enabled: true
}
}
}
});
// Keyboard navigation
let currentIndex = 0;
const dataLength = chart.data.datasets[0].data.length;
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
currentIndex = (currentIndex + 1) % dataLength;
showTooltipAtIndex(currentIndex);
} else if (e.key === 'ArrowLeft') {
currentIndex = (currentIndex - 1 + dataLength) % dataLength;
showTooltipAtIndex(currentIndex);
} else if (e.key === 'Escape') {
chart.setActiveElements([]);
chart.tooltip.setActiveElements([], { x: 0, y: 0 });
chart.update();
}
});
function showTooltipAtIndex(index) {
chart.setActiveElements([{ datasetIndex: 0, index }]);
chart.tooltip.setActiveElements(
[{ datasetIndex: 0, index }],
{ x: 0, y: 0 }
);
chart.update();
// Announce to screen readers
announceDataPoint(index);
}
function announceDataPoint(index) {
const label = chart.data.labels[index];
const value = chart.data.datasets[0].data[index];
const announcement = `${label}: ${value}`;
const liveRegion = document.getElementById('chart-live-region');
liveRegion.textContent = announcement;
}
<div id="chart-live-region" aria-live="polite" class="visually-hidden"></div>
Focusable Canvas
Make the canvas focusable for keyboard users:
<canvas id="myChart" tabindex="0" role="img" aria-label="Interactive sales chart. Use arrow keys to navigate data points."></canvas>
Motion Sensitivity
Respect user preferences for reduced motion to prevent discomfort.
Detecting prefers-reduced-motion
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
animation: prefersReducedMotion ? false : {
duration: 1000,
easing: 'easeOutQuart'
},
transitions: {
active: {
animation: {
duration: prefersReducedMotion ? 0 : 300
}
}
}
}
});
Listening for Preference Changes
const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
motionQuery.addEventListener('change', (e) => {
chart.options.animation = e.matches ? false : { duration: 1000 };
chart.update();
});
Graceful Animation Reduction
Instead of disabling completely, reduce animation:
const animationConfig = prefersReducedMotion
? { duration: 0 }
: {
duration: 1000,
easing: 'easeOutQuart',
delay: (context) => context.dataIndex * 100
};
Data Table Alternatives
Provide tabular data alongside or as alternative to charts.
Hidden Data Table
<figure>
<canvas id="myChart" role="img" aria-label="Quarterly sales chart"></canvas>
<figcaption>
<details>
<summary>View data table</summary>
<table>
<caption>Quarterly Sales Data 2024</caption>
<thead>
<tr>
<th scope="col">Quarter</th>
<th scope="col">Sales ($)</th>
</tr>
</thead>
<tbody>
<tr><td>Q1</td><td>12,000</td></tr>
<tr><td>Q2</td><td>19,000</td></tr>
<tr><td>Q3</td><td>15,000</td></tr>
<tr><td>Q4</td><td>25,000</td></tr>
</tbody>
</table>
</details>
</figcaption>
</figure>
Generating Tables from Chart Data
function generateDataTable(chart) {
const labels = chart.data.labels;
const datasets = chart.data.datasets;
let html = '<table><thead><tr><th scope="col">Category</th>';
datasets.forEach(ds => {
html += `<th scope="col">${ds.label}</th>`;
});
html += '</tr></thead><tbody>';
labels.forEach((label, i) => {
html += `<tr><td>${label}</td>`;
datasets.forEach(ds => {
html += `<td>${ds.data[i]}</td>`;
});
html += '</tr>';
});
html += '</tbody></table>';
return html;
}
Screen Reader Summary Statistics
<div id="chart-summary" class="visually-hidden">
Chart summary: Sales ranged from $12,000 (Q1) to $25,000 (Q4).
Average: $17,750. Total: $71,000. Trend: upward.
</div>
Complete Accessible Chart Example
// Check motion preference
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
// Colorblind-safe palette
const accessibleColors = [
'#0072B2', '#E69F00', '#009E73', '#D55E00'
];
const chart = new Chart(
document.getElementById('accessibleChart'),
{
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: 'Sales ($)',
data: [12000, 19000, 15000, 25000],
backgroundColor: accessibleColors
}]
},
options: {
responsive: true,
animation: prefersReducedMotion ? false : { duration: 1000 },
plugins: {
legend: {
labels: { color: '#1a1a1a' }
},
title: {
display: true,
text: 'Quarterly Sales 2024',
color: '#1a1a1a'
}
},
scales: {
x: { ticks: { color: '#1a1a1a' } },
y: {
ticks: {
color: '#1a1a1a',
callback: (v) => '$' + v.toLocaleString()
},
beginAtZero: true
}
}
}
}
);
<canvas
id="accessibleChart"
role="img"
tabindex="0"
aria-label="Bar chart: Quarterly Sales 2024. Q1: $12,000, Q2: $19,000, Q3: $15,000, Q4: $25,000. Use arrow keys to navigate."
aria-describedby="chart-details"
></canvas>
<div id="chart-details" class="visually-hidden">
Sales increased overall through 2024, with Q4 showing the highest
revenue at $25,000, representing a 67% increase from Q3.
</div>
Additional Resources
- See
references/wcag-compliance.mdfor detailed WCAG requirements and compliance checklist - See
references/colorblind-palettes.mdfor complete colorblind-safe palette options - See
examples/accessible-bar-chart.htmlfor a complete working accessible chart
Related Skills
Xlsx
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Analyzing Financial Statements
This skill calculates key financial ratios and metrics from financial statement data for investment analysis
Data Storytelling
Transform data into compelling narratives using visualization, context, and persuasive structure. Use when presenting analytics to stakeholders, creating data reports, or building executive presentations.
Team Composition Analysis
This skill should be used when the user asks to "plan team structure", "determine hiring needs", "design org chart", "calculate compensation", "plan equity allocation", or requests organizational design and headcount planning for a startup.
Startup Financial Modeling
This skill should be used when the user asks to "create financial projections", "build a financial model", "forecast revenue", "calculate burn rate", "estimate runway", "model cash flow", or requests 3-5 year financial planning for a startup.
Kpi Dashboard Design
Design effective KPI dashboards with metrics selection, visualization best practices, and real-time monitoring patterns. Use when building business dashboards, selecting metrics, or designing data visualization layouts.
Dbt Transformation Patterns
Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or implementing analytics engineering best practices.
Startup Metrics Framework
This skill should be used when the user asks about "key startup metrics", "SaaS metrics", "CAC and LTV", "unit economics", "burn multiple", "rule of 40", "marketplace metrics", or requests guidance on tracking and optimizing business performance metrics.
