A no-bullshit, copy-paste checklist for making your AI-generated charts legally compliant.
The European Accessibility Act is enforced as of June 2025. WCAG 2.1 AA is the legal baseline. If you're vibe coding dashboards and charts, you need to verify every single one against these criteria. This checklist covers the 8 WCAG success criteria most relevant to data visualization. Spend 5 minutes on this now, or spend hours in a lawsuit later.
1.1.1 Non-text Content
Level AEvery chart needs alt text that describes the insight, not just "chart" or "graph". Screen reader users need to understand what the data shows.
Test It
- Right-click the image in your browser
- Select "Inspect" or "Inspect Element"
- Look for the
altattribute - Is it meaningful? Or just generic?
Quick Fix
Use semantic HTML with meaningful alt text:
<figure>
<img src="chart.png"
alt="Q3 revenue grew 23% to $2.1M, driven by platform sales">
<figcaption>Q3 Revenue Breakdown</figcaption>
</figure>
The alt text should answer: what insight does this chart show? Not "bar chart of sales data" but "sales increased from $1.2M to $2.1M in Q3".
1.3.1 Info and Relationships
Level AThe structure and meaning must be available to screen readers, not just visually. If you removed all CSS styling, the chart should still be understandable.
Test It
- Open your chart in browser DevTools
- Disable CSS (Settings or use View > Web Developer > Style Editor to turn off stylesheets)
- Does the page still make semantic sense?
- Is there a data table as a fallback?
Quick Fix
Always pair charts with structured data:
<figure>
<h3>Conversion Funnel</h3>
<img src="funnel.png" alt="Conversion rates: 100% visitors, 45% signup, 12% purchase">
<table>
<tr><th>Stage</th><th>Count</th><th>Rate</th></tr>
<tr><td>Visitors</td><td>10,000</td><td>100%</td></tr>
<tr><td>Signup</td><td>4,500</td><td>45%</td></tr>
<tr><td>Purchase</td><td>1,200</td><td>12%</td></tr>
</table>
</figure>
1.4.1 Use of Color
Level AColor cannot be the only way to distinguish data. Red/green combos fail instantly for colorblind users. Use patterns, labels, or different shapes instead.
Test It
- Print your chart in grayscale
- Use a colorblind simulator (coblis)
- Can you still distinguish every data series?
- Or do colors become indistinguishable?
Quick Fix
Audit your color palette. If you can only distinguish series by color, add one more signal:
- Add labels directly on bars or points
- Use different shapes (circles, squares, triangles)
- Add patterns (stripes, dots, dashes)
- Use colorblind-safe palettes (viridis, okabe-ito)
Test with: Toptal Color Filter
1.4.3 Contrast Minimum
Level AAText must have at least 4.5:1 contrast ratio against its background. Axis labels, legends, annotations, tooltips; all must be readable.
Test It
- Pick any text in your chart (axis label, legend, annotation)
- Use WebAIM Contrast Checker
- Enter the text color and background color (in hex)
- Is the ratio 4.5:1 or higher? (AA standard)
Quick Fix
Safe defaults for dark text on white:
- Dark gray:
#333333or darker (passes AAA) - Avoid light gray:
#999999or lighter (fails) - If background is colored, test each color combo
/* Dark text on light backgrounds */
axis-label-color: #333333; /* Good */
axis-label-color: #888888; /* BAD - only 4.48:1 */
/* Light text on dark backgrounds */
text-color: #f5f5f5; /* Good */
text-color: #cccccc; /* Borderline */
1.4.11 Non-text Contrast
Level AAGraphical elements like bars, lines, points, and borders must have 3:1 contrast against the background. Light ghostly charts fail here.
Test It
- Use WebAIM Contrast Checker on chart elements
- Test a bar color against the background
- Test a line or point against the background
- Is the ratio 3:1 or higher?
Quick Fix
No chart element should be lighter than #767676 on white background.
/* Safe colors on white background */
bar-fill: #2563EB; /* Good - 8.6:1 */
line-color: #16a34a; /* Good - 6.2:1 */
point-color: #dc2626; /* Good - 5.9:1 */
/* Unsafe */
bar-fill: #cccccc; /* FAIL - 2.8:1 */
line-color: #e5e5e5; /* FAIL - 1.7:1 */
2.1.1 Keyboard
Level AAll interactive chart features must work without a mouse. Tab navigation, data point selection, tooltip display. All keyboard-accessible.
Test It
- Put your mouse away. Touch the trackpad/mouse pad with nothing connected.
- Use only Tab to navigate your chart
- Can you reach every interactive element?
- Do tooltips or details appear when you press Enter?
- Is the focus indicator visible? (usually a blue outline)
Quick Fix
Make chart elements keyboard accessible:
<g role="button"
tabindex="0"
aria-label="Q3 revenue: $2.1M"
onkeydown="handleKeyPress(event)">
<rect x="100" y="50" width="50" height="200" />
</g>
<script>
function handleKeyPress(event) {
if (event.key === 'Enter') {
// Show tooltip, drill down, etc.
}
}
</script>
Use interactive SVG elements with tabindex="0", keyboard handlers, and ARIA labels.
2.3.3 Animation from Interactions
Level AAARespect the user's motion preference. If they've enabled "Reduce motion" in their OS settings, your chart animations should stop or become instant.
Test It
- Open your chart in browser
- System Settings (macOS) or Settings (Windows)
- Search for "Reduce motion" or "Accessibility" preferences
- Enable it
- Reload your chart
- Do animations stop or become instant?
Quick Fix
Check for motion preference and disable animations:
/* CSS: Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Normal animation */
.chart-bar {
animation: slideIn 0.6s ease-out;
}
@media (prefers-reduced-motion: reduce) {
.chart-bar {
animation: none;
}
}
4.1.2 Name, Role, Value
Level AInteractive chart elements need proper ARIA attributes so screen readers understand what they are, what they do, and what their current state is.
Test It
- Run Lighthouse audit (DevTools > Lighthouse)
- Or use axe browser extension
- Look for "Name, Role, Value" failures
- Check that all interactive elements have
aria-labeloraria-labelledby
Quick Fix
Add ARIA to interactive chart elements:
<svg role="img" aria-labelledby="chart-title">
<title id="chart-title">Revenue by Quarter</title>
<g role="button"
aria-label="Q1: $500K"
aria-pressed="false"
tabindex="0">
<rect x="0" y="0" width="50" height="100" />
</g>
</svg>
Every interactive element needs: role (what it is), aria-label (what it says), and state (aria-pressed, aria-selected, etc.)
The Full Pass: A Chart That Works
This chart passes all 8 WCAG criteria. Here's what makes it work:
- 1.1.1: Meaningful alt text describing the correlation pattern
- 1.3.1: Semantic HTML with data table fallback showing all points
- 1.4.1: Multiple signals: different marker shapes (circle, square, triangle) plus colors
- 1.4.3: Dark axis labels (#333) on white background; 9:1 contrast
- 1.4.11: Markers and lines in bold colors (#2563EB, #16a34a); 3:1+ contrast
- 2.1.1: Keyboard navigation works; Tab through all points, Enter shows details
- 2.3.3: Animations respect prefers-reduced-motion
- 4.1.2: SVG has proper ARIA roles and labels; screen readers announce data
Summary Checklist Table
| WCAG | Name | Level | Quick Test | Common AI Fail |
|---|---|---|---|---|
| 1.1.1 | Non-text Content | A | Right-click > Inspect > check alt attribute | Generic "chart" or "graph" instead of insight |
| 1.3.1 | Info and Relationships | A | Disable CSS; does it still make sense? | Pure image with no data table fallback |
| 1.4.1 | Use of Color | A | Grayscale print; can you distinguish all series? | Red/green bars; colorblind users see grey |
| 1.4.3 | Contrast Minimum | AA | WebAIM checker; text vs background 4.5:1? | Light gray text; fails at 3:1 ratio |
| 1.4.11 | Non-text Contrast | AA | WebAIM checker; bars vs background 3:1? | Pale ghostly lines on white; barely visible |
| 2.1.1 | Keyboard | A | Tab through chart; reach all elements? | SVG elements not focusable; no keyboard handlers |
| 2.3.3 | Animation from Interactions | AAA | Enable "Reduce motion" in OS; animations stop? | Animations continue despite prefers-reduced-motion |
| 4.1.2 | Name, Role, Value | A | Run axe audit; any failures? | Interactive elements missing aria-label or role |
Run through this checklist before shipping any vibe-coded chart. It takes 5 minutes. A lawsuit takes longer.