Why Shopify Stores Are Getting ADA Lawsuits in 2026 — and How to Fix It in Code
Shopify powers over 4.5 million stores. Plaintiffs' law firms know this — and they run automated scanners against them at scale.
In 2025, over 5,000 ADA web accessibility lawsuits were filed in the US. 69% targeted ecommerce stores. Of those, Shopify stores accounted for 32% — the single most-sued platform by share.
If you build or maintain Shopify stores for clients, one of them will get a demand letter. The fix is almost always in the theme code you wrote or touched. Here's what to look for and how to fix it.
Why Shopify Specifically?
Three reasons Shopify stores fail at scale:
1. Themes optimized for looks, not semantics. Most premium themes use heading tags for visual sizing, not document structure. A store with <h3> styled to look like a page title and <h1> buried in a product card is a screen reader nightmare.
2. Third-party apps inject inaccessible markup. Every popup, review widget, and countdown timer is code you didn't write and probably didn't test. One keyboard trap in a "Spin to Win" modal can make an otherwise clean store legally exposed.
3. AJAX patterns create ghost content. Slide-out carts, Quick View modals, and variant pickers update the DOM without telling screen readers. The user hears nothing. The item was added to cart — silently, invisibly.
The 6 Violations That Show Up in Demand Letters
These are the WCAG failures plaintiffs' scanners flag most consistently on Shopify stores.
1. Product Images Without Alt Text
WCAG 1.1.1 (Level A) — the most common, most fixable
In Shopify Liquid, product images often render like this:
<img src="{{ product.featured_image | img_url: '600x' }}">
No alt attribute. Screen readers announce the filename. Fix it:
<img
src="{{ product.featured_image | img_url: '600x' }}"
alt="{{ product.featured_image.alt | escape | default: product.title | escape }}"
>
The default filter is your safety net — if the merchant didn't fill in alt text in the admin, it falls back to the product title. Not perfect, but never empty.
For purely decorative images (dividers, backgrounds):
<img src="{{ section.settings.bg_image | img_url: '1400x' }}" alt="" role="presentation">
2. Icon Buttons With No Accessible Name
WCAG 4.1.2 (Level A) — cart, search, wishlist, account icons
The most common pattern:
<a href="/cart" class="header-cart">
<svg><!-- cart icon --></svg>
</a>
A screen reader announces this as "link" with no destination or purpose. Fix with aria-label:
<a href="/cart" class="header-cart" aria-label="Shopping cart, {{ cart.item_count }} items">
<svg aria-hidden="true" focusable="false"><!-- cart icon --></svg>
</a>
aria-hidden="true" on the SVG stops the screen reader from trying to read it. focusable="false" prevents IE11 from focusing the SVG directly.
3. Keyboard Traps in Modals and Slide-out Carts
WCAG 2.1.1 (Level A) — the most complained-about pattern
When a modal opens, focus must move inside it. When it closes, focus must return to the trigger. Tab should cycle through elements inside the modal only — not escape to the rest of the page.
A minimal focus trap in vanilla JS:
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstEl = focusableElements[0];
const lastEl = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', function(e) {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === firstEl) {
lastEl.focus();
e.preventDefault();
}
} else {
if (document.activeElement === lastEl) {
firstEl.focus();
e.preventDefault();
}
}
});
firstEl.focus();
}
Call trapFocus(modalElement) when the modal opens. Store the trigger element before opening and return focus to it on close:
const trigger = document.activeElement;
openModal();
trapFocus(modal);
closeButton.addEventListener('click', () => {
modal.close();
trigger.focus(); // return focus to where the user was
});
4. AJAX Cart Updates With No Screen Reader Announcement
WCAG 4.1.3 (Level AA) — "I added it but nothing happened"
When a user adds an item to the cart, sighted users see a counter increment or a slide-out appear. Screen reader users hear nothing.
Fix with an aria-live region. Add this to your theme layout once:
<div
id="cart-notification"
aria-live="polite"
aria-atomic="true"
class="visually-hidden"
></div>
Then write to it after the cart updates:
fetch('/cart/add.js', {
method: 'POST',
body: JSON.stringify({ id: variantId, quantity: 1 }),
headers: { 'Content-Type': 'application/json' }
})
.then(res => res.json())
.then(item => {
document.getElementById('cart-notification').textContent =
`${item.title} added to cart.`;
});
aria-live="polite" queues the announcement after current screen reader output finishes. aria-atomic="true" reads the full message, not just the changed text node.
Your .visually-hidden utility (should be in every project):
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
5. Color Contrast Failures on Sale Badges and Buttons
WCAG 1.4.3 (Level AA) — the fastest to fix, often overlooked
White text on a red "Sale" badge is the classic failure. Most red hues don't reach 4.5:1 contrast against white.
| Combination | Ratio | Pass? |
|---|---|---|
White #fff on Red #e53e3e
|
3.99:1 | ❌ Fail |
White #fff on Dark Red #9b2335
|
7.1:1 | ✅ Pass |
Black #000 on Yellow #ffd700
|
9.5:1 | ✅ Pass |
Check every foreground/background combination in your theme CSS. The fix is usually darkening the background by 20–30% or switching to a dark text color.
6. Form Fields With Placeholder-Only Labels
WCAG 1.3.1 (Level A) — placeholder text is not a label
Placeholder text disappears when typing, has no programmatic association with the input, and typically fails contrast. Yet it's used as a label in nearly every third-party Shopify app form.
Bad:
<input type="email" placeholder="Email address">
Good:
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email" placeholder="name@example.com">
If the design requires no visible label, use visually hidden — not display:none (that hides from screen readers too):
<label for="email" class="visually-hidden">Email address</label>
<input type="email" id="email" placeholder="Email address" autocomplete="email">
How to Audit a Shopify Store in 10 Minutes
Automated scan — Run the store URL through WCAGsafe or axe DevTools. Gets you ~35–40% of issues instantly with plain-English descriptions and WCAG criterion numbers.
Keyboard test — Open the store, put the mouse away. Tab through every interactive element on the homepage, a product page, and the checkout flow. Can you reach everything? Can you complete a purchase?
Screen reader test — NVDA (Windows, free) or VoiceOver (Mac, built-in, Cmd+F5). Navigate a product page and the cart. Listen for image filenames, unlabeled buttons, and silent AJAX updates.
Contrast check — Run the homepage through WebAIM's contrast checker. Prioritize sale badges, CTA buttons, and any text overlaid on images.
The automated scan + keyboard-only test catches 80%+ of what ends up in demand letters.
The One Thing That Won't Fix It
Shopify's App Store has several "accessibility overlay" apps promising one-click ADA compliance. They inject a floating toolbar that claims to fix contrast, add alt text, and improve keyboard navigation dynamically.
They don't work. They mask code problems without fixing the underlying markup. Courts have consistently rejected overlay-based compliance defenses. And plaintiffs' firms specifically scan for overlay presence — because it signals an inaccessible codebase underneath.
Fix the code. There is no shortcut.
What a Demand Letter Actually Cites
The violations cited almost always include:
- A specific element selector (e.g.,
.header-cart > svg) - The WCAG criterion number (1.1.1, 2.1.1, 4.1.2)
- A screenshot from an automated scanner
All of those are detectable by any automated tool. The plaintiff's attorney ran the same scanner you just ran. Fix the violations your audit surfaces and you've removed most of the legal surface area.
Originally published at wcagsafe.com/blog/shopify-ada-compliance
Top comments (0)