How to fix the 10 most common accessibility issues (with code)
Published: 9 min readThe AccessibleOrNot team
The most common accessibility issues on the web are low contrast text, missing alt text, unlabelled form fields, empty links, empty buttons and a missing page language. In WebAIM's February 2026 scan of one million home pages, these six types made up 96% of all errors found. All six can be detected automatically, and most are fixed with one change in a theme file. This post covers those six, plus four issues that block keyboard and zoom users directly, with before and after code.
In short
- According to the WebAIM Million 2026, 95.9% of home pages had automatically detectable WCAG failures, with an average of 56.1 errors per page.
- The six most common (low contrast 83.9%, missing alt text 53.1%, missing form labels 51%, empty links 46.3%, empty buttons 30.6%, missing language 13.5%) are 96% of all errors.
- Most of these errors come from templates: fix one icon button in the product card and thousands of pages are fixed at once.
- The contrast threshold is 4.5:1 for normal text and 3:1 for large text; WCAG 2.2 sets a minimum target size of 24×24 CSS pixels.
- Fixing these closes the part that can be found automatically; keyboard flow and error messages still need a manual test.
What are the 10 most common accessibility issues?
The first six rows are WebAIM's frequency data. The last four were chosen without frequency data, on three criteria: they can be detected automatically, they're fixed in a few lines, and they directly block someone using a keyboard or zoom. The names in brackets are axe-core rule ids; that's how you'll see them in scan reports.
| # | Issue (axe rule) | WCAG | Share of home pages (WebAIM 2026) | Effort |
|---|---|---|---|---|
| 1 | Low contrast text (color-contrast) | 1.4.3 AA | 83.9% | Hours |
| 2 | Missing alt text (image-alt) | 1.1.1 A | 53.1% | Hours |
| 3 | Unlabelled form field (label) | 1.3.1, 4.1.2 A | 51% | Hours |
| 4 | Empty link (link-name) | 2.4.4, 4.1.2 A | 46.3% | Hours |
| 5 | Empty button (button-name) | 4.1.2 A | 30.6% | Hours |
| 6 | Missing page language (html-has-lang) | 3.1.1 A | 13.5% | Minutes |
| 7 | Zoom disabled (meta-viewport) | 1.4.4 AA | No data | Minutes |
| 8 | No skip link or main landmark (bypass) | 2.4.1 A | No data | Minutes |
| 9 | Iframe without a title (frame-title) | 4.1.2 A | No data | Minutes |
| 10 | Hidden but focusable element (aria-hidden-focus) | 4.1.2 A | No data | Hours |
Source: WebAIM, Deque Systems, GitHub
1. How do you fix low contrast text?
Give normal text a contrast ratio of at least 4.5:1 against its background, and large text (24 px and up, or about 18.7 px bold) at least 3:1. The usual problem spots: the struck-through old price, light grey placeholder text, white text on a light banner. Fix the colour in the theme's colour variables, not one element at a time. In the example, #aaaaaa on white is 2.3:1; #767676 passes 4.5:1.
| Code | |
|---|---|
| Before | <s class="price-old" style="color:#aaaaaa">$49.00</s> |
| After | <s class="price-old" style="color:#767676">$49.00</s> |
Shopify's theme guidelines ask for the same thresholds: 4.5:1 for small text, 3:1 for large text and for icons and borders.
2. How do you fix missing alt text?
Add an alt attribute to every <img>. For a product image, describe the product and what sets it apart (colour, angle, model); use alt="" only for decorative images. Alt text shouldn't be a file name or an empty word like "image" or "photo". If a lazy-load script swaps the image, check that the alt attribute is carried over.
| Code | |
|---|---|
| Before | <img src="/products/navy-wool-coat-1.jpg"> |
| After | <img src="/products/navy-wool-coat-1.jpg" alt="Navy wool coat, front view"> |
If you edit in an admin panel rather than code: in Shopify, Products > product > media item > Add alt text; in WooCommerce, Media > Library > "Alternative Text". If a product card wraps the image and the product name in the same link, give the image alt="", or the screen reader reads the name twice.
Source: W3C, Deque Systems, GitHub
3. How do you fix an unlabelled form field?
Connect a visible <label for="..."> to every form field. Placeholder text isn't a label; it disappears once you start typing. Where the design has no room for a visible label (a header search box, for example), use aria-label. Size, colour and quantity selectors follow the same rule.
| Code | |
|---|---|
| Before | <input type="email" name="email" placeholder="Your email address"> |
| After | <label for="newsletter-email">Your email address</label> <input type="email" id="newsletter-email" name="email"> |
4. How do you fix an empty link?
Give links that contain only an icon or image a name: an aria-label, visually hidden text (an sr-only class) or alt text on the image inside the link. Hide the icon with aria-hidden="true". The name should say where the link goes: "Cart", "Our Instagram". Cart, wishlist, social media and logo links are the usual culprits.
| Code | |
|---|---|
| Before | <a href="/cart"><svg class="icon-bag">...</svg></a> |
| After | <a href="/cart" aria-label="Cart (2 items)"><svg class="icon-bag" aria-hidden="true">...</svg></a> |
Source: Deque Systems, GitHub
6. What if the page language is missing?
Add the page language to the <html> element: lang="en" for English pages. Without it, a screen reader may read the text with another language's pronunciation. On a multilingual site, the value should come from the template for the current language. It must be a valid language code: "en" or "en-GB"; "en_GB" and "english" are invalid.
| Code | |
|---|---|
| Before | <html> |
| After | <html lang="en"> |
In Shopify, in layout/theme.liquid: <html lang="{{ request.locale.iso_code }}">. In WordPress themes, in header.php: <html <?php language_attributes(); ?>>; the value comes from Settings > General > Site Language.
Source: Shopify, WordPress Developer Resources
7. What if zoom is disabled?
Remove user-scalable=no and maximum-scale from the viewport meta tag. A customer with low vision who can't pinch to zoom on a phone can't read the price or the size chart. These values are often added to stop iOS zooming in when a form field gets focus; the right fix for that is a 16 px font size on the fields.
| Code | |
|---|---|
| Before | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| After | <meta name="viewport" content="width=device-width, initial-scale=1"> |
Source: Shopify, Deque Systems, GitHub
8. How do you add a skip link and a main landmark?
Add a "Skip to content" link at the very start of the page that becomes visible on focus, and wrap the main content in <main>. A keyboard user then jumps straight to the product with one key instead of tabbing through 40 menu links on every page. The id in the link's href must exist on the page; give the main content tabindex="-1" so focus can move there.
| Code | |
|---|---|
| Before | <body><header>...</header><div class="content">...</div> |
| After | <body><a class="skip-link" href="#content">Skip to content</a> <header>...</header><main id="content" tabindex="-1">...</main> |
9. How do you fix an iframe without a title?
Give every <iframe> a title attribute that says what's inside: "Store location map", "Product video", "Card details". Third-party embeds (YouTube, maps, live chat) usually arrive without one; add it when you paste the code. On a checkout page, the card form is often an iframe; without a name, a screen reader just says "frame".
| Code | |
|---|---|
| Before | <iframe src="https://www.youtube.com/embed/abc123"></iframe> |
| After | <iframe src="https://www.youtube.com/embed/abc123" title="Coat fabric and stitching details video"></iframe> |
Source: Deque Systems, GitHub
What's left after you fix these 10?
You'll have closed the part that can be found automatically: necessary for an accessible site, but not enough. According to Deque's study, automated testing finds about 57% of issues by volume. For the rest, complete your main flow (product, cart, checkout) with a keyboard only and with a screen reader; check that focus is always visible, that you don't get stuck in pop-ups and that error messages are stated in text. The 20-minute protocol is in our post on automated testing, and platform steps are in our checklists.
Source: Deque Systems
Frequently asked questions
What is the most common accessibility issue?
Low contrast text. According to the WebAIM Million 2026, it appears on 83.9% of home pages. Normal text needs 4.5:1 contrast and large text 3:1.
What does an "empty button" error mean?
The button has no name a screen reader can announce. It usually happens with cart, menu or close buttons that contain only an icon, and is fixed with an aria-label or visually hidden text.
Can a plugin or widget fix these issues?
Not reliably. A widget doesn't change your site's source code, and only the site owner knows what an image's alt text or a button's name should be. Make the fixes in your theme and content.
Should I fix these page by page?
No, fix the template first. One fix in a repeated component, such as the product card, header or footer, fixes thousands of pages at once. What's left at page level is mostly content work, such as alt text.
Sources
- The WebAIM Million, 2026 report · WebAIM · February 2026
- axe-core rule descriptions · Deque Systems, GitHub · current
- WCAG 2.2 · W3C · 5 October 2023 (current version)
- Shopify theme accessibility best practices · Shopify · accessed October 2026
- language_attributes() function reference · WordPress Developer Resources · accessed October 2026
- Automated testing study identifies 57 percent of digital accessibility issues · Deque Systems · 10 March 2021
Related articles
Which of these 10 does your site have?
Enter your URL. We group the issues by template and list them with before/after code and the admin panel steps for your platform.