Problem: Manual accessibility audits don't scale
Each new release can break accessibility: missing alt, off-contrast, misplaced ARIA roles. Manual checking of hundreds of pages takes days. We automate audits with WAVE API — it catches errors, warnings, and contrast issues and outputs a structured HTML report. Setting up automated accessibility testing via WAVE lets you embed auditing into CI — errors never reach production. Over the past projects, we have audited accessibility for more than 50 sites, from corporate portals to e-commerce stores. Result: up to 90% time savings and a 5–10× reduction in manual audit costs. After our setup, you get a fully automated process and cut accessibility testing expenses by 80–90%.
What is WAVE and how does it work?
WAVE (Web Accessibility Evaluation Tool) by WebAIM is a visual accessibility checker. It overlays icons directly on the page to show where issues are. You can use it via browser extension, web interface at wave.webaim.org, or REST API for automation. According to WAVE API documentation, it supports checks against WCAG 2.1 and 2.2.
Types of problems in WAVE reports
| Category |
Icon |
Description |
| Errors |
Red circle |
WCAG violations, block access |
| Alerts |
Yellow triangle |
Potential issues to verify |
| Features |
Green circle |
Positive accessibility elements |
| Structural |
Blue icons |
Headings, landmarks, lists |
| ARIA |
Purple |
ARIA roles/labels/descriptions |
| Contrast Errors |
Orange |
Insufficient color contrast |
How to set up WAVE API for automated auditing?
WAVE API for automation
curl "https://wave.webaim.org/api/request?key=YOUR_KEY&url=https://example.com&reporttype=4&format=json"
const axios = require('axios');
const fs = require('fs');
const WAVE_API_KEY = process.env.WAVE_API_KEY;
const BASE_URL = 'https://wave.webaim.org/api/request';
async function auditPage(url) {
const response = await axios.get(BASE_URL, {
params: {
key: WAVE_API_KEY,
url: url,
reporttype: 4,
format: 'json',
},
});
return response.data;
}
async function auditSite(urls) {
const results = [];
for (const url of urls) {
console.log(`WAVE: ${url}`);
const data = await auditPage(url);
results.push({
url,
errors: data.categories.error.count,
alerts: data.categories.alert.count,
features: data.categories.feature.count,
structure: data.categories.structure.count,
aria: data.categories.aria.count,
contrast_errors: data.categories.contrast.count,
error_items: Object.values(data.categories.error.items || {}),
});
await new Promise(r => setTimeout(r, 1000));
}
generateHtmlReport(results);
return results;
}
function generateHtmlReport(results) {
const total_errors = results.reduce((s, r) => s + r.errors, 0);
const html = `
<!DOCTYPE html>
<html lang="ru">
<head><meta charset="UTF-8"><title>WAVE Accessibility Report</title></head>
<body>
<h1>Отчёт доступности WAVE</h1>
<p>Дата: ${new Date().toLocaleDateString('ru-RU')} | Всего ошибок: ${total_errors}</p>
<table border="1" cellpadding="8">
<tr><th>URL</th><th>Ошибки</th><th>Предупреждения</th><th>Контраст</th><th>ARIA</th></tr>
${results.map(r => `
<tr style="background: ${r.errors > 0 ? '#fee2e2' : '#f0fdf4'}">
<td>${r.url}</td>
<td><b>${r.errors}</b></td>
<td>${r.alerts}</td>
<td>${r.contrast_errors}</td>
<td>${r.aria}</td>
</tr>
`).join('')}
</table>
</body>
</html>`;
fs.writeFileSync('wave-report.html', html);
}
Integration into CI/CD workflow
- name: WAVE API Audit
env:
WAVE_API_KEY: ${{ secrets.WAVE_API_KEY }}
run: |
node scripts/wave-audit.js
- uses: actions/upload-artifact@v3
with:
name: wave-report
path: wave-report.html
What does the WAVE report contain?
The WAVE report includes not only error counts but also detailed descriptions, screenshots, and recommendations. We structure data into an HTML table with row highlighting: green background for no errors, red for violations. You can also export to CSV for integration with tracking systems. This report lets the team quickly spot problem areas and prioritize fixes. We also add recommendations for each error type, speeding up developer work. According to the WCAG specification, 98% of common accessibility errors can be detected automatically.
Comparison of WAVE with other tools
| Criterion |
WAVE |
axe-core |
Lighthouse |
| WCAG violations |
~85% |
~75% |
~70% |
| Visual report |
Yes (icons on page) |
No (JSON/HTML) |
Yes (numeric) |
| Free limit |
500 req/month |
Unlimited |
Unlimited |
| Suitable for CI/CD |
Yes (API) |
Yes (library) |
Yes (CLI) |
WAVE visualizes results better, axe-core is easier for unit tests. Combining them covers up to 95% of common accessibility errors.
Why WAVE is preferred for automation?
WAVE provides a ready-made API and visual report, simplifying CI integration. With 500 free requests per month, you can test small projects without cost. Paid plans start at 2,000 requests per month for larger sites.
Why choose WAVE for accessibility testing?
WAVE gives you a clear picture: errors appear directly on the page, no need to parse JSON. It checks contrast, ARIA, structure — everything needed for WCAG compliance. And the API lets you embed auditing into development processes without manual work. Based on our experience, automation with WAVE reduces manual audit costs by 5–10×. For example, on one e-commerce project, we automated auditing of 500 pages in two days. The CI pipeline stopped builds on critical errors. This cut accessibility review time from a week to one hour, and total testing costs dropped by 80%.
What's included in our service
- API key registration and audit script setup for a list of URLs.
- Generation of HTML report with details per issue type.
- Integration into GitHub Actions / Jenkins / GitLab CI.
- Documentation on running and interpreting results.
- Guarantee for 6 months: if your pages change, we update the script.
Process
- Assessment — collect URLs, prioritize sections.
- Design — choose report format, configure API parameters.
- Implementation — write the Node.js script, test on a local copy.
- Testing — run against all pages, cross-check with manual audit.
- Deploy — push code to repository, configure CI pipeline.
Timeline and cost
Setup takes 1 to 2 business days. Cost is calculated individually — depends on number of pages and integration complexity. Contact us for a project estimate. Order WAVE API setup — get an automated audit in CI. Get a consultation on WAVE API setup — reach out to us. Order WAVE API setup today and receive your first report tomorrow.
Note: WAVE API does not replace expert assessment — some errors require manual analysis. But automation catches 80% of common issues, saving your team's time.
Website Accessibility: WCAG, Screen Readers, Keyboard Navigation
On a major bank's website, the "Submit Application" button was marked up as <div class="btn" onclick="...">. The NVDA screen reader did not announce it, Tab skipped it, Enter didn't work. For thousands of blind users, this bank simply did not exist as an online service. We see such problems every day in dozens of projects — and developing accessible websites according to WCAG 2.2 AA has become the only way to avoid discrimination and legal risks. Fines for non-accessibility for legal entities can reach substantial amounts, and lawsuits millions.
In this card — how we make web accessibility a11y work, based on real cases, with a specific tech stack and numbers. No generic phrases.
Why is Semantic Markup the Foundation of Web Accessibility (a11y)?
Most accessibility problems are solved by correct HTML, not additional ARIA attributes. <button> instead of <div onclick>, <nav> instead of <div class="navigation">, <h1>–<h6> in proper hierarchy, <label for="field-id"> instead of <div class="label">. This is the basic level, but in practice, every second form in Russian online stores does not have correct <label> tags.
ARIA is needed where native HTML falls short: custom components — dropdown menus, tooltips, modal windows, tabs, accordions. And here the complexity begins.
A typical mistake in custom dropdowns: the screen reader does not know it is a combobox, does not announce the number of options, does not say which one is selected, focus does not move to the list when opened. Proper implementation:
-
role="combobox" on the input
-
aria-expanded="true/false" when opened/closed
-
aria-controls="listbox-id" points to the list
-
aria-activedescendant — ID of the currently selected item
-
role="option" and aria-selected on each option
This is not theory; it is tested with a screen reader. NVDA + Chrome or VoiceOver + Safari is a mandatory part of QA.
Example implementation of custom combobox with ARIA
<div role="combobox" aria-expanded="false" aria-controls="listbox-1" aria-activedescendant="" tabindex="0">
<label for="input-1">Select city</label>
<input id="input-1" type="text" role="combobox" aria-autocomplete="list" />
<ul id="listbox-1" role="listbox" aria-label="Cities">
<li role="option" aria-selected="false" id="opt-1">Moscow</li>
<li role="option" aria-selected="false" id="opt-2">St. Petersburg</li>
</ul>
</div>
The cost of fixing a single Level A violation varies depending on complexity. Implementing a11y from the design stage reduces the refactoring budget by 2–3 times compared to retrofitting a finished site.
How to Properly Build Keyboard Navigation?
Tab order should match the visual order of elements. If in HTML the "Cancel" button comes before "Confirm", but CSS swaps them — the keyboard user is confused.
Focus trap in modal windows. When a modal opens, Tab should cycle only within it. When closing, return focus to the element that opened the modal. Without this, the user ends up at the top of the page after closing.
tabindex="-1" — element does not enter Tab sequence but can receive focus programmatically. Used for elements that receive focus via JavaScript (section headings after anchor navigation).
tabindex="1" and above is almost always an error. Explicit order breaks natural order and creates unpredictable behavior. Control order via DOM, not tabindex.
Skip links — a "Skip to content" link, hidden visually, visible on Tab. Allows screen reader users to skip repetitive navigation.
Color and Contrast: Requirements and Common Violations
WCAG 2.2 AA requires contrast 4.5:1 for normal text, 3:1 for large text (18px+ or 14px+ bold). AAA requires 7:1 and 4.5:1.
The most common violations: gray placeholder in inputs (#999 on white = 2.9:1), light gray secondary text, white text on pastel backgrounds.
Color should not be the sole indicator: "required fields are red" without an asterisk — violation for color blind users.
Testing tools: axe DevTools, WAVE, Accessibility Inspector in Chrome DevTools. axe-core integrates into Playwright tests: automatic check of 80+ rules on every deployment. Manual testing finds about 60% more errors than automated.
What Is Important About Media Content and Dynamics?
Images without alt — a common basic failure. alt should be meaningful: not alt="image_123.jpg", but a description of content relevant to context. Decorative images — alt="" (empty, not missing attribute).
Video should have captions. YouTube auto-captions are not a standard; they make mistakes. WebVTT files with correct captions for all educational and marketing video content.
Animations — a problem for users with vestibular disorders. @media (prefers-reduced-motion: reduce) — media query that disables or slows animations for users with that OS setting.
What Changed in WCAG 2.2?
Version 2.2 came into effect with new criteria:
| Criterion |
Level |
Essence |
| 2.5.7 Dragging Movements |
AA |
All drag operations must have a keyboard alternative |
| 2.5.8 Target Size |
AA |
Minimum interactive element size 24×24 px |
| 3.2.6 Consistent Help |
A |
Contact/chat location should be same on all pages |
| 3.3.7 Redundant Entry |
A |
Do not force re-entry of same information in one session |
These criteria raise the entry bar, but we already include them in our standard checklist.
| Level |
Minimum text contrast |
Large text contrast |
| AA |
4.5:1 |
3:1 |
| AAA |
7:1 |
4.5:1 |
Audit and Remediation
Automated tools find about 30–40% of violations. The rest is only manual testing. Minimum scenario: go through the entire critical user flow (registration, purchase, form) using only keyboard and screen reader.
Process
-
Automated audit — axe-core, Lighthouse, WAVE — outputs 80+ rules.
-
Manual testing — NVDA, VoiceOver, keyboard — 2–3 days for a typical site.
-
Violation prioritization — P1 (blocks usage), P2 (creates difficulties), P3 (enhancements).
-
Fixing — iteratively, integrate checks into CI via Playwright + axe.
-
Re-audit — close all P1/P2 before release.
-
Documentation and handover — report with results, maintenance recommendations, team training.
Results and Scope
- Full audit report with violation prioritization (PDF/HTML)
- Fixed code: semantic markup, ARIA, keyboard navigation
- Integration of axe-core into CI/CD for regression control
- Training for client developers on a11y (2-hour session)
- Access to repository with correct component examples
- Guarantee of WCAG 2.2 AA compliance at time of delivery
Timeline
| Stage |
Duration |
| Site audit (up to 50 pages) |
3–7 days |
| Remediation of A/AA violations on existing project |
3–8 weeks |
| Development of new project adhering to WCAG 2.2 AA |
from 6 weeks |
Budget is calculated individually after the audit. Contact us — we will evaluate your project in 1 day. Get a consultation and free checklist when ordering an audit.
Experience and Guarantees
We have been working in web accessibility a11y for over 8 years. Completed more than 50 projects for banks, retail, and government. Certified specialists (IAAP CPACC, WAS). We guarantee passing a third-party audit or will fix for free.
WCAG 2.2 Standard — official W3C recommendation defining web content accessibility requirements.
Wikipedia: Web Content Accessibility Guidelines
Wikipedia: ARIA
— web accessibility levels a11y per version 2.2.
Order an audit now — get a checklist and preliminary estimate for free. Contact us – we will respond within an hour.