DEV Community

Auditready
Auditready

Posted on

The 12 HTML checks my accessibility scanner runs — and what each one can't see

The 12 HTML checks my accessibility scanner runs — and what each one can't see

I build AuditReady, an accessibility scanner that runs 12 deterministic checks over a page's HTML and turns the findings into a report an agency can hand a client. Same product as my first post here, so the disclosure stands: it's mine. Here is what each check decides, and where it runs out of road. Every check looks for one pattern, counts how often it occurs, keeps the offending elements and their position, and names the WCAG 2.2 criterion it maps to. Nothing is model-generated.

1. Missing alt text — 1.1.1 (A)

A screen reader announces the file name, or just "graphic", where an image carried meaning. It flags an <img> with no alt attribute (alt="" is valid for decorative images and is never reported) and image buttons with no alt. It cannot judge the alt that exists: alt="image" passes.

2. Form fields with no label — 3.3.2 (A)

The visitor hears "edit text" and does not know what to type. It flags a visible input, select or textarea with no <label for>, wrapping label, aria-label, aria-labelledby or title — <input placeholder="Email"> included, because placeholder text is not a label. It cannot judge whether the label is clear, or whether errors are explained in text.

3. Heading structure — 1.3.1 (A)

People navigate by headings, so the outline has to hold together. Three findings: no <h1>, more than one (often a logo marked up as one), and a skip such as <h2> straight to <h4>. It cannot see whether the heading text says anything useful.

4. Page title — 2.4.2 (A)

The title is the first thing announced and how tabs and bookmarks are told apart. It flags a missing or empty <title> — <title></title> is announced as "untitled document". It cannot tell whether the title is descriptive or unique: the same "Home" on forty pages passes.

5. Page language — 3.1.1 (A)

Without a declared language a screen reader guesses at pronunciation. It flags <html> with no lang, or an empty one. It reads only the root element, so it cannot see a German quotation inside an English page that needs its own lang, or whether the code present is right.

6. Vague link text — 2.4.4 (A)

Screen-reader users pull up a list of links out of context, where "click here" twelve times says nothing. It flags links whose whole label is a known vague phrase or a bare URL; a bare domain in a logo link, like python.org, counts as a brand name. It cannot judge context: "Learn more" inside a card may read perfectly.

7. Pinch-zoom switched off — 1.4.4 (AA)

Someone with low vision pinches to enlarge the text and nothing happens. It flags a viewport meta tag with user-scalable=no or maximum-scale below 2: <meta name="viewport" content="user-scalable=no, maximum-scale=1">. It cannot test what happens when the page is zoomed.

8. Icon buttons with no name — 4.1.2 (A)

The screen reader says "button" and stops, and the icon's meaning is gone. It flags links and buttons whose computed accessible name is empty, checking aria-labelledby, aria-label, inner text, an image alt and an SVG title in turn, so <button aria-label="Search">…</button> is left alone. It cannot judge the name it finds, or whether a toggle exposes its state.

9. Iframes with no title — 4.1.2 (A)

A frame is a whole document inside the page; untitled, the visitor lands inside someone's video or booking form with no explanation. It flags <iframe src="…"> with no non-empty title and no other name. It cannot see a single element inside the frame — third-party embeds are usually the weakest part.

10. Duplicate id values — 4.1.2 (A)

When an id repeats, <label for> and aria-labelledby can point at the wrong element and in-page links land in the wrong place — usually a copy-pasted form block. It reports the repeated placements. It cannot tell whether anything actually breaks, since the page's JavaScript may never touch that id. (WCAG 2.2 dropped the old 4.1.1 Parsing criterion; this is reported as a reference problem.)

11. Positive tabindex — 2.4.3 (A)

tabindex="1" overrides the page's reading order, so tabbing jumps and skips controls, and the order breaks when someone adds an element. It flags any positive tabindex; 0 and -1 are fine. It cannot measure the order that results — a page with no positive values can still tab in a nonsense order.

12. Tables with no headers — 1.3.1 (A)

A table without headers announces "£40, £40, £40" instead of "Price, £40". It flags tables that contain data cells but no <th>, and skips layout tables marked role="presentation". It cannot check whether the headers are scoped correctly.

What none of the twelve can see

Together they are a scanner that reads markup. Everything needing a browser, an ear or a judgement sits outside it. Verbatim from my site: "Automated tools catch only a minority of WCAG issues. AuditReady is not a compliance guarantee and not legal advice — a manual review by a person is still required."

That is most of the job, not a footnote. From the 13-item checklist the full report ships with:

  • Contrast measured (4.5:1, 3:1 for large text) and non-text contrast for icons, borders and focus rings.
  • Focus visibility, focus order, keyboard traps.
  • Custom widgets: operable without a mouse, with name, role, state and status announced.
  • Errors described in text, beside the field, with a suggestion.
  • 200% zoom, 320px reflow and text-spacing overrides; a working skip link.
  • Captions, transcripts and audio description; anything that moves, blinks or autoplays; time limits.
  • Drag alternatives, 24×24 tap targets and single-pointer equivalents for multi-point gestures.
  • Cookie banners, chat widgets, booking engines and PDFs.
  • Everything behind a login, and whether the alt text, link text and titles that exist are accurate.

More than twenty distinct things, none settled by a rule over a string of HTML. Two limits apply on top: no JavaScript is executed, so a client-rendered page shows fewer findings than it has; and one page is a snapshot, not a crawl.

Why start here

A deterministic check is cheap, repeatable and honest about what it did: mechanical failures found before a person spends an afternoon on them, and a list you can verify. The free scan runs all twelve and shows the top five finding groups; the full report (one-off $29 per site) adds every finding, your agency's name and the coverage table. If you run a page you know well and hit a false positive, tell me in the comments.

Top comments (0)