All WordPress HTML Templates Forms & Webhooks AI & Tools
WordPress

WordPress Accessibility: Fixing the Five Failures Auditors Always Find

The five WordPress accessibility failures auditors find on almost every site, with the WCAG 2.2 AA fixes: contrast tokens, focus rings, names, headings, live

A real working moment illustrating the theme of an article about wordpress accessibility. Wide 16:9 banner, one strong focal point, magazine editorial quality, authentic and unstaged.

Run axe DevTools across a freshly built WordPress site and you’ll get a number. Run it across the same site three months after the client has been adding pages, and you’ll get a much bigger number. Almost none of the new failures will be exotic. They’ll be the same five things auditors have been writing up since WCAG 2.0 shipped in 2008.

The WebAIM Million analysis has found detectable WCAG failures on roughly 95% of home pages every year it’s been run, and the ranking of failure types barely moves: low contrast text first, missing alternative text second, then empty links, missing form labels and empty buttons. That’s not a coincidence. Those are the failures that automated tools catch, which means they’re the ones that end up in an audit report with a screenshot and a line item next to them.

So this is the practical version of WordPress accessibility: the five findings that show up in nearly every report we’ve received back from a third party auditor, why WordPress specifically produces them, and the fixes that survive a client editing the site afterwards.

Key Takeaways

  • WCAG 2.2 AA is the target that regulators now reference, including the European Accessibility Act which became applicable on 28 June 2025. The new criteria that catch WordPress sites hardest are 2.4.11 Focus Not Obscured and 2.5.8 Target Size, both of which sticky headers and icon toolbars violate by default.
  • Automated testing finds about a third of real issues. Keyboard-only navigation of your five most important templates finds most of the rest, and takes about 20 minutes per template.
  • Contrast failures are almost always a design token problem, not a page problem. Fix the palette once at the theme level and hundreds of instances disappear.
  • Icon-only buttons, “Read more” links and AJAX filters are the three patterns that generate the majority of accessible-name and status-message failures in WordPress themes.
  • Accessibility overlay widgets do not fix any of this. Several have been named in US lawsuits as part of the complaint rather than the defence.

What an auditor actually tests in 2026

A real audit isn’t a Lighthouse score. The ones we’ve been through involve a tester with NVDA on Firefox and VoiceOver on Safari, a keyboard, a zoom level of 400%, and a spreadsheet mapped to the 56 success criteria of WCAG 2.2 Level AA. They test a sample: home, a landing page, a listing with filters, a single post, the contact form, checkout if there is one.

Automated scanning happens too, but as triage. Deque has said for years that axe catches something in the region of a third to a half of WCAG issues, and that matches our experience. The scanner gives you 40 line items; the human tester gives you 15 more that are individually worse.

The legal context has sharpened. The EAA applies to e-commerce, banking, transport and e-books sold into the EU, and it names EN 301 549, which in turn points at WCAG 2.1 AA as the floor. In the US, the DOJ’s April 2024 rule set WCAG 2.1 AA for state and local government web content. Build to 2.2 AA and you’re ahead of both. The delta between 2.1 and 2.2 is small and mostly in your favour.

Failure one: contrast, and the three places it hides

Body text is rarely the problem. It’s the greys around it: the byline, the caption, the placeholder, the disabled-looking-but-actually-enabled button, the form helper text set at #999 on white. That’s 2.85:1. You need 4.5:1 for text under 24px, or 18.66px bold.

Two places people forget entirely:

  • Non-text contrast (1.4.11): input borders, toggle states, chart segments and icon glyphs need 3:1 against their background. A 1px #e5e5e5 border on a white input is a failure, and it’s in almost every modern form design.
  • Focus indicators (1.4.11 plus 2.4.13 in 2.2): your focus ring needs 3:1 against the adjacent colour. A blue ring on a blue button fails.

Fix it in tokens, not in pages. If your theme exposes CSS custom properties, correct the muted foreground once and the whole site moves:

:root {
  / 4.54:1 on #ffffff, replaces the usual #999 (2.85:1) /
  --text-muted: #6b6b6b;
  / 3.1:1 on #ffffff, the minimum for input borders and dividers /
  --border-strong: #949494;
}

input, select, textarea { border: 1px solid var(--border-strong); }
::placeholder { color: var(--text-muted); opacity: 1; } / Firefox dims placeholders by default /

Dark mode needs its own pass. Pure white text on pure black is 21:1 and causes halation for readers with astigmatism. We ship #e8e8e8 on #15171a instead, which is about 13:1 and noticeably easier to read for everyone. Test both modes. Auditors do.

Failure two: keyboard operability and focus you cannot see

Unplug your mouse. Tab through your home page. If you lose track of where you are, so does every keyboard user, and that’s 2.4.7 failed on the whole site in one shot.

Three WordPress-specific offenders:

The theme’s CSS reset

Somebody wrote *:focus { outline: none; } in 2014 and it has been copy-pasted ever since. Never remove a focus indicator without replacing it. Use :focus-visible so mouse users don’t see rings on click:

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid #0b57d0;
  outline-offset: 2px;
}
/ Safari and older WebKit ignore :focus-visible on some elements, so keep a fallback /
:where(a, button):focus:not(:focus-visible) { outline: none; }

The sticky header

WCAG 2.2 added 2.4.11 Focus Not Obscured (Minimum). If your sticky header covers the element that just received focus, you fail. Browsers scroll focused elements into view at the very top of the viewport, straight under your 80px header bar. One line fixes it:

html { scroll-padding-top: 6rem; } / must exceed the sticky header height /

The mega menu and the off-canvas drawer

Hover-only submenus are unreachable by keyboard. Off-canvas drawers that stay in the tab order while visually hidden send focus into a void. The drawer needs inert when closed, focus moved inside when opened, Escape to close, and focus returned to the trigger. Most commercial themes get two of those four right.

WCAG 2.2 also introduced 2.5.8 Target Size (Minimum): 24 by 24 CSS pixels for pointer targets, with spacing exceptions. Social icon rows in footers routinely ship at 16px with 4px gaps. Pad them.

Failure three: things with no accessible name

Empty links and empty buttons rank in the top five WebAIM failures every year, and WordPress is a reliable source of both. An icon-only hamburger. A search magnifier. A card where the image is the link and the alt is empty. A “Read more” that says nothing about what you’d be reading more of.

<button type="button" class="menu-toggle"
        aria-expanded="false" aria-controls="primary-menu">
  <svg aria-hidden="true" focusable="false" width="24" height="24">...</svg>
  <span class="screen-reader-text">Menu</span>
</button>

focusable="false" matters because older Edge and IE put inline SVGs in the tab order. Use WordPress core’s .screen-reader-text class rather than inventing your own, because plugins target it.

For “Read more”, append the title in a visually hidden span. Two lines in functions.php, applied to every archive on the site:

addfilter( 'excerptmore', function () {
	return sprintf(
		' %2$s: %3$s',
		escurl( getpermalink() ),
		esc_html__( 'Read more', 'yourtheme' ),
		eschtml( getthe_title() ) // gives each link a unique accessible name
	);
} );

Alt text is the other half of this failure. Decorative images take alt="", never a missing attribute and never the filename. Product photos, team headshots and infographics need real descriptions. The editorial rule we hand to clients: describe what a reader would lose if the image didn’t load, in one sentence, and skip “image of”. If you’re already reviewing media as part of image optimisation in WordPress, do the alt audit in the same pass. The Media Library’s list view will show you the gaps fast.

Failure four: heading order and landmark structure

Screen reader users navigate by headings. JAWS and NVDA both let you jump heading to heading, and surveys consistently put headings as the top navigation method. An H4 used because it was the right size, or a page with three H1s because the hero, the logo and the post title all claim it, breaks the map entirely.

Rules that hold up:

  • One H1 per page, matching the page’s subject. On archives that’s the archive title, not the first post.
  • Never skip a level going down. H2 to H4 is a finding. H4 back up to H2 is fine.
  • Landmarks once each: one <header> with role="banner" semantics at top level, one <main>, one <footer>. Multiple <nav> elements need distinct aria-label values (“Primary”, “Breadcrumb”, “Footer”).
  • A skip link as the first focusable element, pointing at #main, and <main id="main" tabindex="-1"> so focus actually lands there in Safari.
  • <html lang="en-GB">. Missing document language is a one-line failure that appears on something like one in six home pages.

The block editor makes this worse because heading level is a dropdown next to font size. Lock it down. Restrict heading levels per block type in theme.json, and give editors a styles palette that only contains compliant combinations. This is the same discipline as building a client-proof WordPress admin: if the wrong choice isn’t available, nobody makes it at 5pm on a Friday.

Failure five: content that changes without telling anyone

This is the one automated tools miss and human auditors always catch. You filter a product archive over AJAX, 12 results become 3, and a screen reader user hears nothing at all. That’s 4.1.3 Status Messages, and in WooCommerce sites it’s usually failed in four places: filters, add-to-cart, coupon application and quantity updates.

The fix is a live region that exists in the DOM on page load. Inject the region and its text at the same time and most screen readers won’t announce it.

<p id="results-status" role="status" class="screen-reader-text"></p>
const status = document.getElementById( 'results-status' );

function announceResults( count ) {
  // role="status" is implicitly aria-live="polite", so it queues rather than interrupts
  status.textContent = count === 0
    ? 'No products match your filters.'
    : ${count} products found.;
}

Form validation is the same problem wearing a different hat. Inline errors need aria-invalid="true", a description wired with aria-describedby, and focus moved to the first invalid field on submit. A summary at the top of the form (“2 errors, review below”) linking to each field is better still, and it’s what most audit reports will ask for. If your forms post to an third-party endpoint rather than PHP, the error handling is entirely yours to build. We’ve written up the patterns in HTML form handling without a backend.

Overlays, and choosing a theme that isn’t fighting you

Skip the overlay widgets. They inject an ARIA layer over markup that’s still broken underneath, they frequently break the user’s own assistive tech, and the accessibility community has been publicly opposed for years. A vendor promising WCAG compliance from one script tag is selling you legal exposure with a nice icon.

Theme choice does most of the work instead. An accessible WordPress theme gets you semantic landmarks, keyboard-operable menus, visible focus and a contrast-checked palette before you write a line of CSS. When evaluating one, tab through the demo, not the feature list. Check the mobile menu, the modal, the carousel and the accordion, because those four components account for most of the keyboard failures we see. Our own Canvas template ships focus-visible styles and semantic navigation markup as defaults for exactly this reason, but the test applies to any theme, including ours. The same evaluation habit applies to performance, which we covered in choosing a theme that won’t wreck your Core Web Vitals.

For ongoing checks, put pa11y-ci in CI and fail the build on regressions:

npx pa11y-ci --sitemap https://example.com/wp-sitemap.xml \
  --sitemap-exclude "/wp-json/" \
  --standard WCAG2AA

That catches contrast, names and language regressions on every deploy. It will not catch focus order, which is why you still tab through the site before launch.

Frequently Asked Questions

Does an accessibility plugin make my WordPress site compliant?

No. Plugins can help with narrow, mechanical tasks such as adding skip links or flagging images with missing alt text, but no plugin can rewrite your theme’s markup, fix focus order or write meaningful alt text. Treat plugins as linting, not remediation. Overlay widgets in particular should be avoided outright.

Which WCAG version should I build to in 2026?

WCAG 2.2 Level AA. EN 301 549 and the DOJ’s 2024 rule currently reference 2.1 AA, and 2.2 is backwards compatible with it, so building to 2.2 satisfies both and covers you when the references update. WCAG 3.0 is still a working draft and is not something you should be targeting for compliance.

How long does fixing these five failures actually take?

On a typical 30 page brochure site with a well built theme, budget 15 to 25 hours: most of it in the contrast pass and the keyboard fixes to menus and modals. On a WooCommerce site with filters, variations and a custom checkout, double it. Content level work such as alt text and heading structure is ongoing rather than a one-time fix.

Is the block editor accessible for content authors?

It’s usable but not comfortable. Keyboard navigation inside the editor has improved a lot since WordPress 6.0, though complex block nesting is still hard to operate with a screen reader. If you have authors using assistive technology, test their specific workflow before you commit to a block heavy template structure, and keep the Classic Editor available as a fallback.

Do I need an accessibility statement?

If you fall under the EAA, the Section 508 rules or the UK public sector regulations, yes, and it has to be specific: which standard you conform to, which parts don’t conform, and how to report a problem. Even when it isn’t required, a dated statement with a real contact route is the cheapest goodwill you can buy, and it gives complainants somewhere to go before they go to a lawyer.

Where to start on Monday

Pick your five highest traffic templates. Run axe DevTools on each and log what it finds. Then unplug the mouse and tab through the same five pages, writing down every moment you lose the focus ring or get stuck. Those two lists, combined, will be about 80% of what a paid audit would tell you, and you’ll have them by lunchtime.

Fix the contrast tokens first, because it’s one change with the widest reach. Then the focus styles. Then names on icon buttons and links. If you only ever do those three, you’ll have cleared the majority of what auditors find, and the site will be measurably better to use for everyone tabbing through it at 11pm on a phone with a cracked screen.

accessible wordpress theme keyboard navigation aria live region ajax wordpress filters fix low contrast text wordpress theme focus not obscured sticky header fix screen reader text read more link wordpress wcag 2.2 aa wordpress compliance wordpress accessibility wordpress accessibility audit checklist