HTML and CSS course Β· Module 10: Accessibility (a11y)

Accessibility Testing

8 min read
In this lesson6

You have built the museum page following every rule from this module. But how do you know it really works? It is easy to overlook a missing alt or a field without a label until you go through the page the way other people do. Even the best knowledge of accessibility cannot replace practical testing. Just as archaeologists systematically search excavation sites, we must systematically test our pages for accessibility using various tools and methods.

Tests go from the fastest to the most demanding: first an automated tool (Lighthouse or axe), then a keyboard test, next a screen reader, and finally tests with users with disabilities. Each stage catches errors that the previous one cannot see.

Automated Tools

Lighthouse (built into Chrome DevTools)

Lighthouse is a free Google tool that can be run directly from Chrome DevTools:

  1. Open DevTools (F12)
  2. Go to the "Lighthouse" tab
  3. Check "Accessibility"
  4. Click "Analyze page load"

Lighthouse checks, among other things:

  • Whether images have alt text
  • Whether color contrast is sufficient
  • Whether form elements have labels
  • Whether the page has correct heading hierarchy

Note: a score of 100 does not mean the page is fully accessible. The tool checks whether an image has an alt, but it cannot judge whether that alt makes sense - which is why manual tests always come next.

axe DevTools

axe is one of the most popular accessibility testing tools - a browser extension built on the axe-core engine, which Lighthouse also uses. It describes every problem in a fixed format:

1<!-- An axe test result might look like this: -->
2<!--
3  Violation: Images must have alternate text
4  Element: <img src="pharaoh.jpg">
5  Fix: Add an alt attribute to the image
6  Impact: Critical
7  WCAG: 1.1.1 (Level A)
8-->

The report says what is wrong, how to fix it, how serious the problem is (Impact) and which WCAG criterion it concerns - here 1.1.1, that is, text alternatives.

HTML Validator (W3C)

Correct HTML is the foundation of accessibility, because readers rely on the element tree built from your code. The validator detects errors that break this tree:

1<!-- Errors detected by the validator: -->
2
3<!-- Error: Missing alt attribute -->
4<img src="photo.jpg">
5
6<!-- Error: Duplicate ID -->
7<input id="name">
8<label id="name">
9
10<!-- Error: Incorrect nesting -->
11<p>Text <div>in a div</div> in a paragraph</p>

A duplicate id breaks for and aria-describedby connections, and a <div> inside a <p> makes the browser close the paragraph earlier than you intended. You will get to know the validator better in the next module.

Manual Testing

Keyboard Test

The simplest manual test: put the mouse aside and go through the whole page with Tab, Shift+Tab, Enter and Escape, ticking off each point:

1Keyboard navigation checklist:
2[ ] Can every interactive element be reached with Tab?
3[ ] Is the focus order logical?
4[ ] Is focus always visible?
5[ ] Do modal dialogs trap focus?
6[ ] Can a modal be closed with the Escape key?
7[ ] Does the skip link work correctly?
8[ ] Can forms be submitted with Enter?
9[ ] Can dropdown menus be navigated with arrow keys?

The test takes a few minutes and detects what an automated tool cannot see, such as focus disappearing into a hidden menu. Also submit an empty form and check whether the messages lead you to the invalid fields.

Screen Reader Test

A screen reader is the only way to hear a page the way a blind person hears it. Popular screen readers:

  • NVDA (Windows, free) - www.nvaccess.org
  • VoiceOver (macOS/iOS, built-in) - Cmd + F5
  • JAWS (Windows, paid) - professional standard
  • TalkBack (Android, built-in)

VoiceOver or the free NVDA is enough to start - listen to whether fields have meaningful names. The last and most valuable stage is testing with users with disabilities - nobody knows their needs better than they do.

Zoom Test

Visually impaired people zoom in on a page instead of squinting at small text. You can zoom with Ctrl and + (Cmd and + on a Mac):

1Zoom test checklist:
2[ ] Zoom the page to 200% - is all content visible?
3[ ] Zoom to 400% - does the page still work?
4[ ] Does text not get cut off or overlap?
5[ ] Is scrolling only in one direction?

The thresholds come from WCAG: criterion 1.4.4 requires text to be resizable to 200% without loss of content, and 1.4.10 requires no scrolling in two directions at a width of 320 CSS pixels, which is 400% on a 1280px screen.

Chrome DevTools - Accessibility

Accessibility Panel

DevTools will show you how the browser "translates" an element for a screen reader. Select an element in the Elements panel and open the Accessibility tab:

1Chrome DevTools > Elements > Accessibility:
2- Name: "Close" (from aria-label)
3- Role: button
4- Focusable: true
5- Description: "Close dialog"

Name is the element's accessible name, and Role says what it is. An empty Name means the reader has nothing to read - you have just found a bug.

Vision Deficiency Simulation

DevTools can also show the page through the eyes of a person with a vision impairment. Chrome DevTools > Rendering > Emulate vision deficiencies:

  • Protanopia (no red)
  • Deuteranopia (no green)
  • Tritanopia (no blue)
  • Blurred vision

You open the Rendering panel from the DevTools menu (More tools). Turn on protanopia and see whether the red error border still stands out.

Contrast Inspection

You can check contrast without leaving DevTools. In the Styles tab, click the color swatch next to the color property:

1Chrome DevTools > Elements > Styles:
2- Click the color swatch next to the color property
3- The color picker will show the Contrast ratio
4- Check mark next to AA = text meets the 4.5:1 threshold
5- Check mark next to AAA = also meets the 7:1 threshold
6- No check mark = contrast too low

You can move the color there until the contrast reaches the required threshold.

Automating a11y Tests

eslint-plugin-jsx-a11y (React)

When you start writing React in the Space Mission world, the ESLint linter with the jsx-a11y plugin will catch accessibility errors while you are still writing code:

1{
2  "plugins": ["jsx-a11y"],
3  "rules": {
4    "jsx-a11y/alt-text": "error",
5    "jsx-a11y/anchor-has-content": "error",
6    "jsx-a11y/label-has-associated-control": "error",
7    "jsx-a11y/no-noninteractive-element-interactions": "warn"
8  }
9}

Each rule is one check - alt-text reports an image without alternative text, and label-has-associated-control a field without a label. The "error" level treats a problem as an error, while "warn" only warns.

axe-core in Tests

You can also run the axe-core engine in automated tests, so that every change in the code is checked without your involvement:

1<!-- Example of an axe-core test result: -->
2<!--
3  Rule: color-contrast
4  Description: Elements must have sufficient color contrast
5  Help: Fix any of the following:
6    Element has insufficient color contrast of 2.5:1
7    Expected minimum: 4.5:1
8  Target: .subtitle
9-->

The color-contrast rule found text with a contrast of 2.5:1 where 4.5:1 is required and pointed to the .subtitle selector - you know exactly what to fix.

Accessibility Checklist - Complete List

Finally, the checklist for the whole module, ordered from the foundations - semantics and page language - to advanced ARIA attributes:

1<!-- HTML Structure -->
2<!-- [ ] Correct heading hierarchy (h1-h6) -->
3<!-- [ ] Semantic tags (header, nav, main, footer) -->
4<!-- [ ] Page language (lang="en") -->
5
6<!-- Images -->
7<!-- [ ] Alt text for informational images -->
8<!-- [ ] Empty alt="" for decorative images -->
9
10<!-- Navigation -->
11<!-- [ ] Skip link -->
12<!-- [ ] Visible focus -->
13<!-- [ ] Logical Tab order -->
14
15<!-- Forms -->
16<!-- [ ] Label for every field -->
17<!-- [ ] Error messages -->
18<!-- [ ] aria-required for required fields -->
19
20<!-- Color and Contrast -->
21<!-- [ ] Contrast minimum 4.5:1 -->
22<!-- [ ] Information not conveyed by color alone -->
23
24<!-- Multimedia -->
25<!-- [ ] Captions for video -->
26<!-- [ ] Transcripts for audio -->
27
28<!-- ARIA -->
29<!-- [ ] aria-label for icon buttons -->
30<!-- [ ] aria-live for dynamic content -->
31<!-- [ ] aria-expanded for expandable elements -->

Go from the top: without semantics and lang, even the best ARIA will not help. For icon buttons, also check that the icon itself has aria-hidden="true", so the reader reads only the name from aria-label.

Accessibility testing is like verifying that every corridor in the pyramid leads where it should - systematically checking every element to ensure that every visitor reaches their destination. In the final project of this module you will go through this list on your own page.

Practice

Go through the interactive checklist in the editor below point by point:

Code for this lesson: index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Accessibility testing - checklist</title>
7    <link rel="stylesheet" href="style.css">
8</head>
9<body>
10    <h1>Accessibility testing checklist</h1>
11
12    <section>
13        <h2>1. Keyboard test</h2>
14        <p>Navigate this page using <kbd>Tab</kbd> and check that:</p>
15        <ul class="checklist">
16            <li><label><input type="checkbox"> Every interactive element can be reached with Tab</label></li>
17            <li><label><input type="checkbox"> Focus is visible on every element</label></li>
18            <li><label><input type="checkbox"> The focus order is logical</label></li>
19            <li><label><input type="checkbox"> Buttons respond to Enter and Space</label></li>
20        </ul>
21    </section>
22
23    <section>
24        <h2>2. HTML structure test</h2>
25        <ul class="checklist">
26            <li><label><input type="checkbox"> The page has a correct h1-h6 hierarchy</label></li>
27            <li><label><input type="checkbox"> Semantic tags are used</label></li>
28            <li><label><input type="checkbox"> The page language is set (lang="en")</label></li>
29            <li><label><input type="checkbox"> All IDs are unique</label></li>
30        </ul>
31    </section>
32
33    <section>
34        <h2>3. Image test</h2>
35        <div class="test-area">
36            <figure>
37                <img src="https://placehold.co/300x200/d4af37/1a1a2e?text=Sphinx"
38                     alt="The Great Sphinx of Giza guarding the pyramids">
39                <figcaption>The Great Sphinx - an image with alt text</figcaption>
40            </figure>
41            <img src="https://placehold.co/400x3/d4af37/d4af37" alt="">
42            <p><em>The decorative line above has alt=""</em></p>
43        </div>
44        <ul class="checklist">
45            <li><label><input type="checkbox"> Informative images have a descriptive alt</label></li>
46            <li><label><input type="checkbox"> Decorative images have alt=""</label></li>
47        </ul>
48    </section>
49
50    <section>
51        <h2>4. Form test</h2>
52        <form class="test-form">
53            <div class="form-group">
54                <label for="test-name">Name:</label>
55                <input type="text" id="test-name" aria-required="true">
56            </div>
57            <div class="form-group">
58                <label for="test-email">Email:</label>
59                <input type="email" id="test-email"
60                       aria-describedby="test-email-hint">
61                <p id="test-email-hint" class="hint">Format: john@example.com</p>
62            </div>
63            <button type="button" onclick="validateForm()">Check the form</button>
64            <div id="form-result" aria-live="polite"></div>
65        </form>
66        <ul class="checklist">
67            <li><label><input type="checkbox"> Every field has a label</label></li>
68            <li><label><input type="checkbox"> Required fields are marked with aria-required</label></li>
69            <li><label><input type="checkbox"> Errors are described with aria-describedby</label></li>
70        </ul>
71    </section>
72
73    <section>
74        <h2>5. Contrast test</h2>
75        <div class="contrast-test">
76            <p class="pass-aa">This text meets WCAG AA (contrast > 4.5:1)</p>
77            <p class="fail">This text does NOT meet the requirements (contrast < 3:1)</p>
78        </div>
79        <ul class="checklist">
80            <li><label><input type="checkbox"> Text contrast of at least 4.5:1</label></li>
81            <li><label><input type="checkbox"> Information is not conveyed by color alone</label></li>
82        </ul>
83    </section>
84
85    <script>
86    function validateForm() {
87        const name = document.getElementById('test-name').value;
88        const result = document.getElementById('form-result');
89        if (!name) {
90            result.innerHTML = '<p class="error" role="alert">Fill in the Name field!</p>';
91        } else {
92            result.innerHTML = '<p class="success">The form is valid!</p>';
93        }
94    }
95    </script>
96</body>
97</html>

Remember: an automated tool is the first guard at the pyramid's entrance, but only walking the corridors with a keyboard and a screen reader will show whether everyone reaches the chamber.

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Where can you run the Lighthouse tool to test accessibility?

  2. 2. What does the media query @media (prefers-reduced-motion: reduce) do?

Hands-on tasks in the game

  • Vertical ordering

    Arrange accessibility testing steps from automated to manual:

  • Vertical ordering

    Arrange the correct structure of a figure element with a description:

  • Vertical ordering

    Arrange the correct media query to disable animations:

  • Code editor

    Test the form - click Submit without filling in the fields. Check how aria-invalid, aria-describedby, and role="alert" communicate errors.

  • Click in order

    Arrange a correct error message with ARIA attributes:

  • Vertical ordering

    Arrange a11y checklist items from most important (fundamentals) to advanced:

  • Horizontal ordering

    Arrange the correct syntax of a current page link with aria-current:

  • Click in order

    Arrange a correct button with an icon and aria-label:

  • Vertical ordering

    Arrange landmark roles in the typical order on a page (from top to bottom):

  • Code editor

    Find and fix all 8 accessibility errors on this page. Each error is marked with a comment.

Useful articles