Shopify Accessibility: Fix the 9 Most Common WCAG Violations Before They Cost You


If you run a Shopify store, you're building on top of someone else's theme code — which means you're also inheriting someone else's accessibility mistakes. Most Shopify themes, custom or off-the-shelf, repeat the same handful of WCAG violations in the same handful of places: product galleries, add-to-cart buttons, filter sidebars, checkout forms, cart drawers.

This matters for two reasons. First, the obvious one: real people using screen readers, keyboard navigation, or voice control can't buy from a store that fights them at checkout. Second, the legal one: e-commerce sites account for roughly 70% of all ADA digital accessibility lawsuits in the US (UsableNet, "ADA Web Lawsuit Trends for 2026," based on 2025 filings), and federal courts saw 3,117 website accessibility lawsuits filed in 2025 alone — up 27% from 2,452 in 2024 (ADA Title III / Seyfarth Shaw, March 2026). Shopify alone powers close to 30% of US e-commerce (Shopify, "The small business shakedown"), which puts a lot of stores in the blast radius of a trend that's still climbing, not leveling off.

None of this means installing a widget and calling it done — an accessibility overlay doesn't touch the underlying markup your theme renders, and it won't fix any of the nine issues below. (We've written about why overlays don't reduce lawsuit risk if you want the deeper version of that argument.) The fixes below work at the HTML/CSS/ARIA level, which means they apply whether you're running Dawn, a purchased theme, or something fully custom — you're fixing what actually gets rendered in the DOM, not writing Liquid.

A quick honest note before we start: fixing these nine won't make your store "100% ADA compliant" — nobody can promise that, and you should be skeptical of anyone who does. What it will do is close the gaps that show up most often in real audits and are most likely to be the difference between a store that works for everyone and one that doesn't.

1. Product images with no alt text (or bad alt text)

Product photography is the first thing a screen reader user needs described, and it's the single most common miss.

<!-- Broken: no alt attribute -->
<img src="/products/blue-runner-shoe-side.jpg">

<!-- Broken: alt text that's just the filename -->
<img src="/products/blue-runner-shoe-side.jpg" alt="blue-runner-shoe-side.jpg">

<!-- Fixed: describes what's actually shown -->
<img src="/products/blue-runner-shoe-side.jpg"
     alt="Blue running shoe, side view, showing mesh upper and white sole">

For product galleries with multiple angles of the same item, don't repeat the same alt text on every thumbnail — describe what's different about each shot (side view, sole detail, worn on foot), or mark repeats as decorative (alt="") if the surrounding thumbnail button already has an accessible name.

2. Add-to-cart and quick-buy controls that aren't real buttons

Many themes style a <div> or <a> to look like a button for "Add to Cart" or "Quick Buy," which strips out keyboard access and screen-reader semantics.

<!-- Broken: div with a click handler, unreachable by keyboard -->
<div class="btn-add-to-cart" onclick="addToCart()">Add to Cart</div>

<!-- Fixed: a real button, keyboard-operable and announced correctly -->
<button type="submit" class="btn-add-to-cart">Add to Cart</button>

If it triggers an action, it should be a <button>. If it navigates somewhere, it should be an <a href="...">. Nothing else gives you free keyboard support and correct screen-reader announcement ("button," "link") for that price.

3. Sale badges and price call-outs that fail contrast

"SALE," "-30%," and "New" badges are usually the most stylized text on the page — and often the least readable, especially in the pastel-on-white or white-on-light-gray combos popular in theme design.

/* Broken: ~2.3:1 contrast ratio, fails WCAG AA (needs 4.5:1 for normal text) */
.badge-sale { color: #ff8fa3; background: #fff5f5; }

/* Fixed: ~5.7:1 contrast ratio */
.badge-sale { color: #c4133c; background: #fff0f2; }

Run any badge, price, or "low stock" label through a contrast checker before shipping a new theme color scheme — these small UI elements get redesigned more casually than body text, and it shows.

4. Filter and facet sidebars with no ARIA state

Collection-page filters (size, color, price range) are usually collapsible sections. Without ARIA, a screen reader has no way to know whether a filter group is open or closed, or that it's a filter at all.

<!-- Broken: no indication of expanded state or relationship -->
<div class="filter-header" onclick="toggleFilter()">Size</div>
<div class="filter-options">...</div>

<!-- Fixed: real button, ARIA state, and a programmatic link between them -->
<button class="filter-header" aria-expanded="false" aria-controls="filter-size">
  Size
</button>
<div id="filter-size" class="filter-options" hidden>...</div>

Toggle aria-expanded and the hidden attribute together in your JS. That's the whole fix — no new markup structure needed.

5. Checkout fields missing labels and autocomplete

Checkout is the highest-stakes moment on the whole site, and it's where placeholder-as-label and missing autocomplete attributes cost the most.

<!-- Broken: placeholder disappears on input, no label, no autocomplete -->
<input type="text" placeholder="Email address">

<!-- Fixed: persistent label, correct autocomplete token -->
<label for="checkout-email">Email address</label>
<input type="email" id="checkout-email" autocomplete="email">

autocomplete isn't just a browser-fill convenience — WCAG 2.2's Redundant Entry and Accessible Authentication criteria specifically target reducing how much users have to re-type, which matters most for people using switch access or voice input.

6. Cart drawers and quick-view modals with no focus trap

The slide-out cart and the "quick view" product modal are both dialogs, and both are frequently built without dialog semantics — so keyboard focus leaks behind the overlay, and Escape does nothing.

<!-- Broken: a styled div with no role, no focus management -->
<div class="cart-drawer">
  <button class="close-btn">×</button>
  ...
</div>

<!-- Fixed: proper dialog role, labeled, keyboard-dismissible -->
<div class="cart-drawer" role="dialog" aria-modal="true" aria-labelledby="cart-heading">
  <h2 id="cart-heading">Your cart</h2>
  <button class="close-btn" aria-label="Close cart">×</button>
  ...
</div>

Pair the markup with JS that moves focus into the drawer when it opens, traps Tab inside it, and returns focus to the trigger button on close.

7. Product image carousels with no pause control or keyboard nav

Auto-rotating carousels on product and homepage banners are a double violation risk: moving content with no pause button fails WCAG 2.2.2, and arrow-only navigation with no keyboard equivalent fails 2.1.1.

<!-- Broken: autoplay, no pause, mouse-only arrows -->
<div class="carousel" data-autoplay="true">
  <div class="carousel-arrow-prev" onclick="prev()"></div>
  <div class="carousel-arrow-next" onclick="next()"></div>
</div>

<!-- Fixed: pause control, real buttons, reachable by keyboard -->
<div class="carousel" data-autoplay="true">
  <button class="carousel-pause" aria-label="Pause carousel">Pause</button>
  <button class="carousel-arrow-prev" aria-label="Previous slide">‹</button>
  <button class="carousel-arrow-next" aria-label="Next slide">›</button>
</div>

If the carousel is purely decorative marketing content, the simplest fix is often to just turn off autoplay entirely — it also tends to help conversion, not just accessibility.

8. Color/size swatches that rely on color alone

Variant swatches (especially color) frequently convey the selected state only through a border color change or a subtle highlight — which fails for anyone with low vision or color blindness.

<!-- Broken: only a color-based ring shows which swatch is selected -->
<button class="swatch swatch--navy" style="border-color: blue;"></button>

<!-- Fixed: text-equivalent state, not just color -->
<button class="swatch swatch--navy" aria-pressed="true">
  <span class="visually-hidden">Navy — selected</span>
</button>

The visual ring can stay — just make sure aria-pressed (or aria-selected, depending on the widget pattern) and an accessible name carry the same information for anyone who can't see the color difference.

9. "Sold out" and stock-level badges shown only as color or icon

A red dot or a grayed-out swatch meaning "out of stock" is invisible information to a screen reader, and easy to miss for low-vision users too.

<!-- Broken: grayscale + a small icon is the only signal -->
<div class="swatch swatch--sold-out"><svg class="icon-x"></svg></div>

<!-- Fixed: same visual treatment, plus a text equivalent -->
<div class="swatch swatch--sold-out">
  <span class="visually-hidden">Sold out</span>
</div>

This one is a five-minute fix per template and closes one of the more common "why can't I tell what's in stock" complaints in accessibility audits of retail sites.

Where AccessGuard fits in

You don't have to catch all nine of these by hand on every template, every time you update a theme. AccessGuard scans your live store, flags violations like the ones above against WCAG 2.1/2.2 AA (and AAA where relevant), and generates a starting-point code fix for each one — so your job shifts from hunting for problems to reviewing and shipping fixes.

Paste your Shopify store URL and run 3 free scans — no card required. Start your free scan →

Start scanning for free

Join thousands of developers making the web more accessible.

Get started