Topic 7.6
Validating and Testing HTML
In one line
Quality comes from layers of checks: the W3C validator for correct HTML, Lighthouse and axe for automatic accessibility checks, a keyboard and screen reader pass for what tools can't judge, and the DevTools accessibility tree to see what assistive tech really gets.
Think of it like this
Getting a car ready for a long trip. A machine at the garage reads the engine's error codes in seconds (that's the automatic tools). But only a test drive tells you the seat is uncomfortable and the mirror is loose (that's manual testing). You need both, and the machine check comes first because it's cheap.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Validator
- A tool that checks your HTML against the HTML standard and lists errors and warnings.
- Nu HTML Checker
- The W3C's official HTML validator, at validator.w3.org/nu.
- Lighthouse
- A Chrome tool that scores a page for performance, accessibility, best practices and SEO.
- axe-core
- An open-source engine of accessibility rules, used by axe DevTools, Lighthouse and test libraries.
- Automated testing
- Checks a program runs for you, like axe in a test suite. Fast, but finds only some problems.
- Manual testing
- A person checking the page with keyboard, zoom and a screen reader. Slower, but finds what tools miss.
- Rotor
- VoiceOver's menu (VO+U) that lists headings, links, landmarks and form controls to jump between.
- Reflow
- Content re-arranging to fit a narrow or zoomed screen without sideways scrolling (WCAG 1.4.10).
Step by step
01Validate the HTML
Paste your page into validator.w3.org/nu (or run vnu.jar locally). Fix errors from the top: one early error, like an unclosed element, often causes many later ones. Here is real output for a page with two mistakes:
Error: Element div not allowed as child of element ul in this context. (Suppressing further errors from this subtree.)
From line 14, column 3; to line 14, column 7
Error: Duplicate ID nav.
From line 22, column 1; to line 22, column 15
Error: An img element must have an alt attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.
From line 30, column 5; to line 30, column 4202Run Lighthouse
DevTools → Lighthouse → tick Accessibility → Analyze page load. Or from a terminal. The report lists failed audits with the elements involved, plus a list of things to check manually.
03Add axe to your tests
An automated test that fails when new violations appear stops regressions before they ship. This Playwright test checks the home page against WCAG A and AA rules.
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";
test("home page has no detectable WCAG A/AA violations", async ({ page }) => {
await page.goto("http://localhost:3000/");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
.analyze();
expect(results.violations).toEqual([]);
});04Do the keyboard and zoom pass
Tab from the address bar to the footer and back. Then zoom to 400% (or set the window to 320 px wide): text should wrap, menus should collapse, nothing should be clipped.
05Listen with a screen reader
Start NVDA or VoiceOver. Open the headings list (NVDA+F7, or VO+U on Mac): does it read like a table of contents? Jump between landmarks. Tab into the form and submit it empty: are errors announced?
06Inspect the accessibility tree
When something sounds wrong, select the element in DevTools and read the Accessibility pane: Name, Role, and properties like focusable, required, invalid, expanded. The "Name" row shows which source (label, aria-label, contents) produced it.
Try it yourself
- 1
Make it valid
In the first example, change the
divinside the paragraph to aspanand move thedivin the list into anli. Predict the three log lines before running. (1 paragraph,truebecause thespan class="box"now stays inside it, andLI, LI.) - 2
Clean the lint
In the second example, fix the HTML so the log shows a clean outline (H1, then H2s with H3s under them), no duplicate ids (
[]) andlang: en. - 3
Audit a real page
Open any site you've built (or a favourite site), run Lighthouse's accessibility audit and the axe DevTools extension. Compare: do they report the same problems? Then pick one failure and find the element in the Accessibility pane.
Code & diagrams
A `<p>` can't contain a `<div>`, so the parser closes the paragraph before the div, and the stray `</p>` creates a second, empty paragraph. The `div` in the `ul` is kept but is invalid: a screen reader may count the list wrongly. The validator reports both.
Preview
Console
Nothing logged yet.Expected console output
paragraphs written: 1, paragraphs in the DOM: 2
div is inside the p? false
children of ul: DIV, LIReal tools check hundreds of rules like these. Fix the HTML until there are no jumps, no duplicates and a language is set.
Preview
Console
Nothing logged yet.Expected console output
H1 Recipes
H3 Breakfast <- skips a level
H2 Lunch
H4 Soups <- skips a level
duplicate ids: [ 'note' ]
lang: (missing)Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Trust the Lighthouse score
Ship a page that scores 100 in Lighthouse accessibility, with alt="image" on every product photo and a menu that only opens on mouse hover.
Break #2
Duplicate an id
Copy a form field block and forget to change id="email" and for="email" in the copy.
Myth vs fact
Myth
A Lighthouse score of 100 means the page is accessible.
Fact
It means none of its automatic audits failed. Many WCAG criteria can only be checked by a person.
Myth
If it renders correctly, the HTML is valid.
Fact
Browsers repair errors silently. The DOM may differ from your source, which breaks selectors, scripts and accessibility.
Myth
You need to be an expert screen reader user to test.
Fact
A few keys (headings list, landmarks, Tab, arrows) catch most big problems. Expert audits and testing with disabled users come on top.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Validation is not an end in itself (WCAG 2.2 removed 4.1.1 Parsing), but the errors that matter are real bugs: duplicate ids break label and ARIA references, bad nesting changes the DOM and the accessibility tree, and unknown attributes are often typos (
aria-lable). Lint in the editor (html-validate, eslint-plugin-jsx-a11y for React) to catch them before review. - ▸
axe-core is designed for zero false positives: when it's unsure, it reports "needs review" (incomplete) instead of a violation. In CI, fail on violations and log incompletes for humans.
- ▸
Chrome's accessibility tree is built lazily and cached; heavy DOM changes cause tree updates that cost time on huge pages. Very large DOMs (Lighthouse warns above about 1,400 elements) slow both rendering and assistive tech.
- ▸
Testing with actual disabled users finds problems no checklist does (confusing wording, tiring flows). The W3C WAI site has guidance on involving users, and WebAIM's screen reader survey shows which screen reader and browser combinations to prioritise.
Remember this
- 1
Validation checks your HTML against the HTML standard. The W3C Nu HTML Checker (validator.w3.org/nu) accepts a URL, an uploaded file or pasted code and lists errors (rule breaks, like a
divinside aul, a duplicateid, a missingalt) and warnings (allowed but probably wrong, like a redundantrole). Browsers repair invalid HTML silently (Topic 0.4), so a page can look fine while its DOM is different from what you wrote, which confuses CSS, JavaScript and assistive tech. - 2
Lighthouse, built into Chrome DevTools (the Lighthouse panel) and available as a command-line tool, audits a page for performance, accessibility, best practices and SEO, giving each a score from 0 to 100. Its accessibility audits are a subset of axe-core. A score of 100 means no automatically detectable problems were found, not that the page is accessible.
- 3
axe (axe-core, by Deque) is the most widely used accessibility rules engine. Use it as the free axe DevTools browser extension while building, and in automated tests (
@axe-core/playwright,jest-axe, Cypress plugins) so every pull request is checked. Each finding has a rule id (likeimage-alt,label,color-contrast,button-name), the failing element, the WCAG criterion and a fix. Other tools: WAVE (WebAIM's visual overlay), pa11y (command line) and html-validate (a linter for HTML source). - 4
Automated tools find only part of the problem. Studies differ on how much: a UK government test in 2017 found that the tools caught roughly 30–40% of deliberately planted barriers, and Deque reports that automated tests found about 57% of issues by volume in its audits. Tools can tell that an
altexists, but not whether it's right; that focus is visible, but not whether the order makes sense. Manual testing fills the gap. - 5
The five-minute manual test: (1) keyboard: Tab through everything, use Enter, Space, arrows and Esc (Topic 7.3); (2) zoom the page to 200% and 400%: nothing should be cut off and you shouldn't need to scroll sideways at a 320 px wide view (WCAG 1.4.10 Reflow); (3) screen reader quick pass: list the headings and landmarks, tab through the form, and listen to what's announced; (4) check images and links out of context: do the alt texts and link texts make sense on their own?; (5) turn off CSS or view in Reader mode: does the order still make sense?
- 6
Screen readers to test with: NVDA (free, Windows; the NVDA key is Insert;
Hnext heading,Dnext landmark,Knext link,Fnext form field,NVDA+F7lists elements), VoiceOver (built into macOS and iOS;Cmd+F5toggles it on Mac; the VO keys areCtrl+Option;VO+Uopens the rotor of headings, links and landmarks), Narrator (Windows,Ctrl+Win+Enter) and TalkBack (Android; swipe right to move, double-tap to activate). The most common pairings in WebAIM's screen reader survey are NVDA or JAWS with Chrome on Windows, and VoiceOver with Safari on Apple devices. - 7
DevTools shows what assistive tech gets. In Chrome: Elements → Accessibility pane lists the selected node's computed name (and where it came from), role, and states; the "Enable full-page accessibility tree" option lets you switch the whole Elements panel to the accessibility tree. The Rendering panel can emulate vision deficiencies (blurred vision, reduced contrast, protanopia, deuteranopia, tritanopia, achromatopsia) and
prefers-reduced-motion. The colour picker in the Styles pane shows the contrast ratio with AA/AAA ticks. Firefox's Accessibility Inspector can "Check for issues" and "Show Tabbing Order" as numbered overlays.
Explain it without notes
Why validate HTML if browsers display invalid HTML anyway?
What can automated accessibility tools catch, and what can't they? How do you test the rest?
How would you add accessibility checks to a team's development process?
What does the DevTools accessibility pane show, and when do you use it?
Practice
Write a Playwright test that loads /signup and fails if axe finds any WCAG A or AA violations.
This HTML has three validation errors. List them and write the fixed version: <ul><div><li>One</li></div></ul><img src="a.png"><label for="x">Name</label><input id="y">
Trade-offs
- ↔
Failing CI on every axe violation keeps quality high but can block urgent releases on legacy pages; many teams fail on new violations only and burn down the existing ones over time.
- ↔
Manual screen reader testing is slow, so do it on templates and components (once per pattern) rather than on every page.
Done when you can
I can validate a page with the W3C Nu HTML Checker and fix its errors.
I can run Lighthouse and axe and read their findings.
I can add an axe check to an automated test.
I can do a keyboard, zoom and quick screen reader pass.
I can read the name, role and states in the DevTools Accessibility pane.