Your theme is probably fine. Your media library probably isn't.
Enter your site address. We read the HTML WordPress serves, name the theme and any page builder behind it, and check the things only a WordPress site has — Media Library images, the menu toggle, the search and comment forms, your form plugin's fields and the social row — under the criterion each problem sits beneath.
Free, instant, no signup and no card. Nothing is stored.
Free, instant, no signup and no card. Any page works — the home page, a post, or an archive. We read the HTML your server sends for that one URL. Nothing is stored.
Why WordPress fails in two different places
Most accessibility checkers read a page. A WordPress site is not a page — it is a database rendered through a template, and the two halves fail for different reasons and get fixed in different places. Sending the whole list to a developer is the most expensive mistake an owner makes here, because half of it is not a code change at all.
The template half is your theme and your plugins: the hamburger with no name, the search box with a placeholder instead of a label, the social row of five links called nothing, the contact form whose label fields were left empty. Fix each one once and it is fixed on every page the template renders, forever. It is the cheap half, and on a core theme most of it is already done.
The content half is your Media Library, and no theme change touches it. WordPress prints an alt attribute only when the media record has one, so a site on Twenty Twenty-Four — the theme with every accessibility fix core has shipped — still carries one level A failure per undescribed image, and a blog with 600 images carries 600 of them. That is the half most owners have never looked at, and the half a demand letter opens with, because it takes ten seconds to demonstrate.
The three answers this checker refuses to give
wp-login.php is not graded. If the URL you give us lands on WordPress's login form, the only markup we can read is core's. Scoring it would produce a grade for Automattic's form and tell you nothing about your theme — so we say what happened and stop.
A coming-soon placeholder is not graded either. SeedProd, WP Maintenance Mode and their kin serve their own page instead of your theme. We detect them by their own signature, not by the words “coming soon” in a title — a real site is allowed a page called that, and refusing to grade it would be the false positive.
And a site we cannot confirm is WordPress is not graded. Every rule here is written against WordPress's own markup. Run them on something else and they all go quiet, and a page of silence reads as a clean bill of health. That is the one way a free checker can be actively harmful, so when the signals are absent we say so instead.
And within a real WordPress site, an empty alt attribute is never called a failure. Beside a caption that already describes the image, or inside a card link that already carries the post title, alt="" is the correct answer. It goes in its own bucket for you to walk, and never into the violation count.
What the checker looks for
Seven failures, each naming its criterion, then the review and verify items — real things worth your attention that need judgement or a rendered page, reported separately and never counted as violations.
- Media Library image with no alt attribute at allFailureWCAG 2.1 SC 1.1.1 Non-text Content (Level A)
- The image is served out of /wp-content/uploads/ — something somebody uploaded through the Media Library — and the tag carries no alt attribute. Not an empty one: none. WordPress only prints an alt attribute when the Media Library record has one, so a missing attribute means the field behind that image has never been filled. A screen reader falls back to reading the file name aloud, which on WordPress is usually something like IMG_4821-1024x683.jpg. This is the most common automated failure on the web and the first one a demand letter names, because anybody can demonstrate it in a browser in ten seconds.
- The fix: Alt text lives in your media record, not your theme — no theme change fixes it. Media → Library → click the image → Alternative Text, and it corrects every post that image already appears in. For images already placed in a block, the Image block's sidebar has the same field. If your theme or builder is stripping the attribute, the template is emitting a hand-written <img> instead of wp_get_attachment_image().
- Mobile menu toggle with an icon and no accessible nameFailureWCAG 2.1 SC 4.1.2 Name, Role, Value (Level A)
- The hamburger button that opens your navigation on a phone contains nothing but an icon — an SVG, a font glyph or three styled spans — and carries no aria-label, no title and no screen-reader text. It announces as “button” with nothing after it. On a phone this is the entire navigation of the site behind one unnamed control, and phones are the majority of most WordPress traffic.
- The fix: Put real text inside it rather than an aria-label, so speech-recognition users can say the word they see: <button class="menu-toggle"><span class="screen-reader-text">Menu</span><svg aria-hidden="true" focusable="false">…</svg></button>. In a classic theme this is header.php; in a block theme the core Navigation block already ships the label and a hand-rolled replacement is usually what removed it.
- Search field with only a placeholder for a labelFailureWCAG 2.1 SC 3.3.2 Labels or Instructions / SC 4.1.2 (Level A)
- The site search input — WordPress's own field, name="s" — has no label element pointing at it and no accessible name of its own. A placeholder is not a label: it is not read as one by every screen reader, it disappears the moment the user types, and it fails anyone who needs to re-check what a half-filled field was asking for. WordPress's own get_search_form() emits a real label; a theme that shows a bare box has removed it.
- The fix: Restore the label and hide it visually rather than deleting it: <label for="s" class="screen-reader-text">Search for:</label>. Core's markup in wp-includes/theme-compat/searchform.php is the reference. If a plugin or builder widget renders the box, its own settings usually expose a label field that is empty.
- Comment form field with no labelFailureWCAG 2.1 SC 3.3.2 Labels or Instructions (Level A)
- One of the comment form's fields — the comment textarea, or the name, email or website input WordPress names author, email and url — has no label element and no accessible name. Core's comment_form() ships all four labelled; almost every case of this is a theme that replaced them with placeholders to save vertical space. The email field is the one that hurts: a screen reader user filling in a form that announces nothing has no way to tell which box wants their address.
- The fix: In the theme, pass real labels through comment_form()'s fields argument rather than overriding them with placeholder-only markup, and keep the textarea's <label for="comment">. Hiding the label with .screen-reader-text is fine; removing it is not.
- Contact-form field with no labelFailureWCAG 2.1 SC 3.3.2 Labels or Instructions (Level A)
- A field inside a form-plugin form — Contact Form 7, WPForms, Gravity Forms, Ninja Forms, Formidable or Fluent Forms — has no label element pointing at it and no name of its own. This is the form your site actually converts on, and the plugins are not at fault: every one of them has a label field per input, and every one of them will happily render the input when that field is left empty and a placeholder is typed instead. A form that announces nothing is a form that cannot be completed without sight.
- The fix: Open the form in the plugin's builder and fill the label field on every input, then set the label to visible or to the plugin's own screen-reader-only option. In Contact Form 7 that means wrapping each tag in a real <label>; in WPForms and Gravity Forms it is the Label field on each element, with “hide label” chosen only when something else on screen names it.
- Social icon link with no accessible nameFailureWCAG 2.1 SC 4.1.2 Name, Role, Value (Level A)
- A link in a social-icon row whose entire contents are an icon and which carries no text, no aria-label and no title. It announces as “link” — a row of five of them announces as five links called nothing, in a footer, next to each other. Core's Social Icons block puts the service name in screen-reader text; a theme or builder widget that hand-rolls the row usually does not.
- The fix: Give each link the service name as visually-hidden text inside the anchor — <a href="…"><span class="screen-reader-text">Facebook</span><svg aria-hidden="true" focusable="false">…</svg></a> — and mark the icon aria-hidden. If you are on a block theme, replacing the hand-built row with the core Social Icons block fixes all of them at once.
- No page language on the <html> elementFailureWCAG 2.1 SC 3.1.1 Language of Page (Level A)
- The page does not declare what language it is in, so a screen reader reads it in whatever voice the user last had — English prose in a Spanish voice, or the reverse. WordPress emits this for you: language_attributes() in header.php prints lang="en-US" from the site's own setting. A page missing it has had that call removed or hard-coded away, usually in a hand-converted HTML template.
- The fix: In a classic theme, header.php's opening tag should be <html <?php language_attributes(); ?>>. In a block theme WordPress prints it and you should not need to touch anything — if it is missing there, a plugin is filtering the output. Set the site language under Settings → General.
- No skip link at the top of the pageReviewWCAG 2.1 SC 2.4.1 Bypass Blocks (Level A)
- We found no “skip to content” link in the first part of the body. Without one, a keyboard user tabs through the whole header — logo, every top-level menu item, every submenu item, the search box — on every single page before reaching the article. WordPress core has shipped one in every default theme since Twenty Fourteen. It is review rather than failure here because a skip link can legitimately be injected later in the markup or by script, which a static read cannot see.
- The fix: Add it as the first focusable thing in the body and give the target an id: <a class="skip-link screen-reader-text" href="#content">Skip to content</a>, with id="content" on the <main>. Core's .screen-reader-text CSS already makes it appear on focus — do not use display:none, which removes it from the tab order entirely.
- Repeated “Read more” links with no post titleReviewWCAG 2.1 SC 2.4.4 Link Purpose (In Context) (Level A)
- Several links on this page say the same thing — Read more, Continue reading, Learn more — with nothing distinguishing them. Sighted readers get the purpose from the post above each one; a screen reader user pulling up a list of the page's links gets a column of identical entries. This is review rather than failure because 2.4.4 allows the surrounding context to supply the purpose, and in a post loop it usually does. It is still the single most reported complaint about WordPress archive pages.
- The fix: Core's own themes solve this without changing the visible text: <a href="…">Read more<span class="screen-reader-text"> about “Post title”</span></a>. In a classic theme the filter is the_content_more_link or excerpt_more; in a block theme the Read More block has a text field per query loop.
- Page builder markup on the pageReview
- A visual builder rendered this page. That is not a violation and this checker does not treat it as one — builders can be used accessibly and plenty of sites do. What is true is that their default widgets are where the failures above cluster: the icon widget that emits a link with no text, the heading widget that lets you pick any level so pages end up starting at h4, the button widget that renders a div, and the form widget whose label field is optional. If the list above is empty on this page, it is worth checking a page built with a different set of widgets.
- The fix: Nothing to fix by removing it. Walk one page of each template with the keyboard, check that headings descend in order, and make sure every icon widget has its accessibility text field filled — in Elementor that is the icon's Accessible Name, in Divi the module's Title & Text options.
- Accessibility overlay widget runningReview
- A third-party accessibility overlay is loading on this page. It is reported here as a risk finding, not a violation — keeping it is a business decision. What it does not do is remove a single line from the list above: missing alt text lives in your media records, an unlabelled contact form lives in the plugin's settings, and an unnamed menu toggle lives in your theme. An overlay is a script that runs afterwards and paints a toolbar over the result. US lawsuits filed in 2023 and 2024 name plaintiffs who hit the barrier with the overlay running.
- The fix: Nothing here is a code change. Decide whether you are paying for it to fix the site or to look like the site is fixed, then fix the findings above in the theme, the media records and the form plugin, where they actually are.
- Media Library image marked decorativeVerify
- The image carries alt="" — an explicit statement that it conveys nothing a reader needs. That is frequently the right answer and it is never counted as a violation here: beside a caption that already describes it, or inside a card link that already carries the post title, an empty alt is correct and adding text would make the page noisier. It is listed so you can walk it, because the other way WordPress produces an empty alt is somebody clearing the field to make a plugin's warning go away.
- The fix: Walk the list. Anything a reader needs from the picture — the words in a screenshot, the figures on a chart, the product in a photo — needs a description in Media → Library → Alternative Text. Anything that is genuinely a flourish beside text that already says it should stay exactly as it is.
Where each fix actually lives
Four of these are admin screens, not code. Check them before you book a developer.
- Image alt textMedia → Library → the image → Alternative Text
- Contact form labelsYour form plugin's builder — the Label field per input
- Overlay widgetPlugins — a business decision, not a fix
- Page builder widget namesElementor: icon → Accessible Name. Divi: module → Title & Text
- Menu toggle nameheader.php, or the core Navigation block
- Search field labelsearchform.php — <label for="s" class="screen-reader-text">
- Comment field labelscomment_form()'s fields argument in the theme
- Social icon namesThe Social Icons block, or screen-reader-text in the template
- Page languageheader.php — <html <?php language_attributes(); ?>>
- Skip linkheader.php + id="content" on <main>
Common questions
- Does the ADA apply to a WordPress site?
- US courts have overwhelmingly treated a business website as a place of public accommodation under Title III, and the platform it runs on has never been part of the analysis. What matters is whether a customer who uses a screen reader or a keyboard can do what everybody else can do. WordPress runs something like two in five of all websites, so it is also the platform most demand letters happen to land on — not because it is worse, but because it is what is there. Automattic is responsible for core; your theme, your plugins and your media records are yours.
- Isn't WordPress accessible out of the box?
- Core is the strongest part of the stack. The admin is actively tested, and every default theme since Twenty Fourteen ships a skip link, a labelled search form, a labelled comment form and a named navigation toggle. Three things are yours regardless: the alt text on every image, which only exists if somebody typed it into the Media Library; the plugins you install, which inject markup nobody reviewed; and any theme or builder work done on top. A site on Twenty Twenty-Four with 400 undescribed images has 400 level A failures and a perfectly accessible theme.
- Will an accessibility plugin fix this?
- It depends entirely on which kind. A plugin that scans your content and helps you fill in alt text is doing real work. An overlay widget — the ones that put a little accessibility icon in the corner and a toolbar of font-size controls — cannot fix any of the failures on this page. Missing alt text lives in your media records, an unlabelled contact form lives in that plugin's settings, and an unnamed menu toggle lives in your theme; an overlay is a script that runs afterwards and paints over the result. This checker reports an installed overlay as a review item, not a violation, but it does not reduce the list above by one line.
- Does Elementor or Divi make my site inaccessible?
- No, and this checker never scores a builder as a violation. Both can be used accessibly and plenty of sites do it. What is true is that their default widgets are where the failures cluster: the icon widget that emits a link with no text, the heading widget that lets you pick any level so a page ends up starting at h4, the button that renders as a div, and the form widget whose label field is optional and whose placeholder field is right next to it. If this page comes back clean, check a page built from a different set of widgets before concluding the site is clean.
- Why does it say my site isn't WordPress?
- We look for the signals WordPress puts in its own markup: /wp-content/ and /wp-includes/ asset paths, the generator meta, the wp-json REST route, core block classes, the emoji script, core body classes, and the powered-by and Link response headers. Security plugins remove the generator meta, which is why we read seven other things as well. If none of them are present the honest answer is that we cannot confirm it, so we stop rather than run WordPress checks that would find nothing and report the silence as a pass. A headless front end on the REST API is the usual reason. Run the full scan instead; it renders the page.
- I gave it my site and it says it read the login page.
- Then the URL you gave redirected to wp-login.php — usually a site that is set to require login, or a maintenance plugin that bounces logged-out visitors. That form is core's markup, not yours, so grading it would produce a score for WordPress rather than for your theme. Same with a coming-soon plugin: if SeedProd or WP Maintenance Mode served the page, your theme never ran and there is nothing of yours in the response. In both cases the checker says what happened rather than inventing a grade.
- What can't this tell me?
- Anything that needs a rendered page. Colour contrast between your button and its background, whether focus is trapped inside the mobile menu once it opens, whether your cookie banner can be dismissed with a keyboard, the reading order your CSS produces, and everything behind a click. Those are the scan's job, and it is free too. This page answers what is decidable from the markup your server sends — which on WordPress is most of it, because PHP renders on the server rather than in the browser.
- Does this store my site's URL?
- No. The check is a single HTTP GET of the page you name, run inside the request and discarded when it answers. There is no account, no email field and no row written anywhere. If you go on to run the free scan, that one does keep a report so you can come back to it.
Keep reading
- Running a store instead? Check Shopify →
- Which images have no alt text? →
- Which form fields have no label? →
- What is your hamburger button called? →
- How many links say “read more”? →
- Can a keyboard skip your header? →
- Is an overlay running on your site? →
- The ADA compliance checker →
- Is my website ADA compliant? →
- The WCAG compliance tool →