The Vibe Coder's WCAG Checklist for Data Visualization

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 A
Plain English:

Every 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

  1. Right-click the image in your browser
  2. Select "Inspect" or "Inspect Element"
  3. Look for the alt attribute
  4. 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 A
Plain English:

The 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

  1. Open your chart in browser DevTools
  2. Disable CSS (Settings or use View > Web Developer > Style Editor to turn off stylesheets)
  3. Does the page still make semantic sense?
  4. Is there a data table as a fallback?
Pass: Clear Structure
Funnel chart with clear labels and structure
Has semantic HTML, clear labels, and accompanying data table. Structure survives without CSS.

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 A
Plain English:

Color 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

  1. Print your chart in grayscale
  2. Use a colorblind simulator (coblis)
  3. Can you still distinguish every data series?
  4. Or do colors become indistinguishable?
Fail: Color Only
Survey results bar chart using only red and green colors
Red/green is the classic accessibility fail. 8% of men cannot distinguish these colors. Users see grey bars.
Pass: Color + Labels
Same survey data with blue and orange bars, clear labels
Distinct colors (blue/orange), explicit labels on each bar, legend. Works in grayscale and for colorblind users.

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 AA
Plain English:

Text must have at least 4.5:1 contrast ratio against its background. Axis labels, legends, annotations, tooltips; all must be readable.

Test It

  1. Pick any text in your chart (axis label, legend, annotation)
  2. Use WebAIM Contrast Checker
  3. Enter the text color and background color (in hex)
  4. Is the ratio 4.5:1 or higher? (AA standard)

Quick Fix

Safe defaults for dark text on white:

  • Dark gray: #333333 or darker (passes AAA)
  • Avoid light gray: #999999 or 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 AA
Plain English:

Graphical elements like bars, lines, points, and borders must have 3:1 contrast against the background. Light ghostly charts fail here.

Test It

  1. Use WebAIM Contrast Checker on chart elements
  2. Test a bar color against the background
  3. Test a line or point against the background
  4. Is the ratio 3:1 or higher?
Fail: Low Contrast
Area chart with light gray line on white, barely visible
Light gray line on white background is barely visible. Fails 3:1 contrast. Hard to see for users with vision loss.
Pass: Strong Contrast
Same area chart with strong blue line, clear markers, and labels
Strong blue (#2563EB) passes 3:1 easily. Markers and labels make data clear. Readable in all conditions.

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 A
Plain English:

All interactive chart features must work without a mouse. Tab navigation, data point selection, tooltip display. All keyboard-accessible.

Test It

  1. Put your mouse away. Touch the trackpad/mouse pad with nothing connected.
  2. Use only Tab to navigate your chart
  3. Can you reach every interactive element?
  4. Do tooltips or details appear when you press Enter?
  5. 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 AAA
Plain English:

Respect 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

  1. Open your chart in browser
  2. System Settings (macOS) or Settings (Windows)
  3. Search for "Reduce motion" or "Accessibility" preferences
  4. Enable it
  5. Reload your chart
  6. 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 A
Plain English:

Interactive chart elements need proper ARIA attributes so screen readers understand what they are, what they do, and what their current state is.

Test It

  1. Run Lighthouse audit (DevTools > Lighthouse)
  2. Or use axe browser extension
  3. Look for "Name, Role, Value" failures
  4. Check that all interactive elements have aria-label or aria-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:

Accessible scatter plot with different marker shapes, clear axis labels, legend, and high contrast
  • 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.