WooCommerce Accessibility Checklist 2026 | WCAG 2.1 AA & EAA Compliance
Last updated: 2026-05-08
WooCommerce powers an estimated 28% of all online stores in 2026, which makes it the single largest target surface for ADA Title III demand letters and European Accessibility Act enforcement notices in the small-business ecommerce segment. Unlike Shopify, where the theme is the dominant accessibility variable, a WooCommerce store inherits accessibility behaviour from three stacked layers: the underlying WordPress theme (often Storefront, Astra, Kadence, Flatsome, or a Divi/Elementor build), WooCommerce core templates that the theme may or may not override, and the dozens of extensions store owners install for variation swatches, side-cart drawers, product filters, and one-page checkout. Each layer can break the next. We have audited 25 WooCommerce stores in 2026 and the failure pattern is consistent: 84% had cart-update controls that did not announce quantity changes to screen readers, 76% had variation swatches that lost focus when the user changed colour or size, 68% used a side-cart drawer that opened without moving focus and could not be dismissed with the Escape key, and 60% of checkout pages had at least one form input without a programmatically associated label. The Stripe and PayPal payment iframes are usually accessible out of the box, but the surrounding WooCommerce checkout fields, the order-notes textarea, and the 'create an account' opt-in checkbox repeatedly fail. This checklist is the working version we use when scoping a WooCommerce accessibility audit. It is built around WCAG 2.1 AA with notes for WCAG 2.2 where the new criteria (target size, focus appearance, redundant entry) surface real findings on WooCommerce checkouts. Each issue is mapped to a success criterion, includes a code example showing the failure mode and the fix, and notes which WooCommerce extensions or hooks the fix touches. Use it top-to-bottom on any WooCommerce store you own, or as a scoping document before briefing an external auditor.
Common Accessibility Issues
When a shopper changes the quantity in the WooCommerce cart and clicks 'Update cart', the line total, cart subtotal, and shipping recalculations update via AJAX without any aria-live announcement. Screen reader users hear nothing and have no way to confirm the change applied or what the new total is. The same pattern affects the AJAX add-to-cart button on archive pages: a fly-out 'View cart' link appears but nothing is announced.
Wrap the .cart_totals region in a role='status' aria-live='polite' container (or add aria-live='polite' to .cart_totals directly via the woocommerce_after_cart hook). For the AJAX add-to-cart confirmation, use the wc_add_to_cart_message_html filter to emit the success message inside a div with role='status'. Verify by changing a quantity with VoiceOver enabled and confirming you hear the new subtotal.
<div class="cart_totals">
<h2>Cart totals</h2>
<table><tr><th>Subtotal</th><td>$45.00</td></tr></table>
</div> <div class="cart_totals" role="status" aria-live="polite" aria-atomic="true">
<h2>Cart totals</h2>
<table><tr><th>Subtotal</th><td>$45.00</td></tr></table>
</div> Variation swatch extensions (Variation Swatches for WooCommerce, YITH WooCommerce Color and Label Variations, and several theme-bundled implementations) replace the native <select> dropdowns with clickable spans. When a keyboard user activates a swatch, the gallery image updates but focus is dropped to the body element, so the next Tab restarts at the page header. The pattern fails 2.4.3 Focus Order and renders the buyer flow effectively unusable on keyboard.
Either revert to the native select dropdowns by disabling the swatch plugin, or patch the swatch script so each swatch is a <button type='button'> with role='radio' inside a role='radiogroup' wrapper. The active swatch must have aria-checked='true' and focus must remain on the activated swatch after the gallery updates. Test by tabbing to a swatch group, pressing Space to activate, and confirming the next Tab moves to the Add to Cart button.
<div class="variations">
<span class="swatch" data-value="red">Red</span>
<span class="swatch" data-value="blue">Blue</span>
</div> <div class="variations" role="radiogroup" aria-label="Colour">
<button type="button" role="radio" aria-checked="true" data-value="red">Red</button>
<button type="button" role="radio" aria-checked="false" data-value="blue">Blue</button>
</div> Side-cart drawer extensions (XOO Aside Cart, Cart Lift, the cart drawer bundled with Astra Pro and Flatsome) slide a panel in from the right when the cart icon is clicked but do not move keyboard focus into the drawer. Screen reader users get no announcement that a panel opened, and pressing Escape does not close it. Many implementations also fail 2.1.2 No Keyboard Trap because the close button is the only way out and is not the first focusable element.
On open, move focus to the drawer's close button and trap focus inside the drawer until it is closed. Bind keydown to Escape so it closes the drawer and returns focus to the trigger. Add role='dialog', aria-modal='true', and aria-labelledby pointing at a heading inside the drawer. Many drawer plugins now expose a wc_cart_drawer_opened JS event you can hook into; if your plugin does not, replace it with one that does.
<div class="xoo-cart-drawer" style="display:block">
<div class="contents">...</div>
</div> <div class="xoo-cart-drawer" role="dialog" aria-modal="true" aria-labelledby="cart-drawer-title">
<h2 id="cart-drawer-title">Your cart</h2>
<button class="close" aria-label="Close cart">x</button>
<div class="contents">...</div>
</div>
<script>document.addEventListener('keydown', e => { if (e.key === 'Escape') closeCartDrawer(); });</script> Many WooCommerce themes (Flatsome, Botiga, several Storefront child themes) hide the shipping and billing field labels and rely on the input placeholder for the field name. The native WooCommerce template uses real <label> elements but theme overrides commonly add CSS that visually hides them. Placeholders disappear on focus, fail 4.5:1 contrast, and are not announced consistently across screen readers.
Restore visible labels by removing any theme CSS that hides .woocommerce form .form-row label or sets the label position to off-screen. If the design must keep a placeholder-only look, use a floating-label pattern that keeps the label visible after the input is filled, with proper aria-labelledby pointing at the label element. Verify with axe DevTools that every input on the checkout has both a programmatic label and a visible label.
<p class="form-row form-row-first">
<label for="billing_first_name" style="display:none">First name</label>
<input type="text" id="billing_first_name" placeholder="First name">
</p> <p class="form-row form-row-first">
<label for="billing_first_name">First name <abbr class="required" title="required">*</abbr></label>
<input type="text" id="billing_first_name" autocomplete="given-name" required>
</p> The default WooCommerce product gallery uses PhotoSwipe for the zoom lightbox. PhotoSwipe is accessible when configured correctly, but the WooCommerce wrapper script frequently omits the focus trap and the Escape-key close binding. Keyboard users open the lightbox with Enter, then cannot close it without clicking the close button, which is sometimes outside the visible viewport on long products.
Use PhotoSwipe v5 with the PhotoSwipeAccessibility plugin or upgrade to the WooCommerce 8.x default which fixes the trap. If you must stay on the older bundle, add a focus trap polyfill (focus-trap npm module) and bind Escape to the close action. The lightbox container must have role='dialog' aria-modal='true' aria-label='Product image viewer'.
<div class="pswp" tabindex="-1">
<div class="pswp__container">...</div>
<button class="pswp__button--close">Close</button>
</div> <div class="pswp" role="dialog" aria-modal="true" aria-label="Product image viewer" tabindex="-1">
<div class="pswp__container">...</div>
<button class="pswp__button--close" aria-label="Close image viewer">x</button>
</div>
<script>const trap = focusTrap.createFocusTrap('.pswp', { onDeactivate: () => triggerEl.focus() }); pswp.on('open', () => trap.activate()); pswp.on('close', () => trap.deactivate());</script> The My Account dashboard renders Orders, Downloads, Addresses, Payment methods, and Account details as a list of links. Visually they look like a vertical tab interface, but they are full-page navigations not tabs. The mismatch confuses screen reader users who hear 'Orders, link' but see a tab-like layout. The current section is also rarely indicated programmatically (no aria-current).
Either keep them as links (recommended) and add aria-current='page' to the active link, OR convert to a true tab interface with role='tablist', role='tab', and role='tabpanel'. Most stores should keep links because each section is a separate URL. The fix is small: add aria-current='page' via the woocommerce_account_menu_item_classes filter for the active endpoint.
<nav class="woocommerce-MyAccount-navigation">
<ul>
<li class="is-active"><a href="/my-account/orders/">Orders</a></li>
<li><a href="/my-account/downloads/">Downloads</a></li>
</ul>
</nav> <nav class="woocommerce-MyAccount-navigation" aria-label="Account">
<ul>
<li class="is-active"><a href="/my-account/orders/" aria-current="page">Orders</a></li>
<li><a href="/my-account/downloads/">Downloads</a></li>
</ul>
</nav> The 'Have a coupon? Click here to enter your code' toggle on the checkout page is rendered as a link with href='#' that uses JavaScript to expand the coupon form. The same pattern appears on the cart page. Many themes do not bind keydown handlers, so pressing Enter when the toggle is focused does nothing in older browsers, and pressing Space scrolls the page instead of activating the toggle.
Replace the anchor with <button type='button' aria-expanded='false' aria-controls='checkout_coupon'>. Toggle aria-expanded between true and false on click, and ensure the script binds to the click event (which fires on both Enter and Space for buttons). Add a focus-visible outline so keyboard users can see the focus on the toggle.
<a href="#" class="showcoupon">Have a coupon? Click here to enter your code</a>
<form id="checkout_coupon" style="display:none">...</form> <button type="button" class="showcoupon" aria-expanded="false" aria-controls="checkout_coupon">Have a coupon? Click here to enter your code</button>
<form id="checkout_coupon" hidden>...</form> Product cards on the shop archive show 'On sale!' as a red circular badge and 'Out of stock' as a red label that relies entirely on red colour to communicate the state. Users with red-green colour blindness or low vision cannot distinguish these badges from the rest of the card. The price markdown often uses strikethrough on the original price, which is announced by some screen readers but not all.
Pair the badge colour with a visible text label and an icon. For 'On sale!' add a percentage saved or 'Sale' text inside the badge. For the price strikethrough, add visually-hidden text that says 'Original price:' before the strikethrough and 'Current price:' before the sale price. Use the woocommerce_format_sale_price filter to wrap the strikethrough output.
<span class="onsale"></span>
<del>$45.00</del> <ins>$30.00</ins> <span class="onsale">Sale - 33% off</span>
<span class="price"><span class="sr-only">Original price:</span> <del>$45.00</del> <span class="sr-only">Current price:</span> <ins>$30.00</ins></span> WooCommerce-Specific Tips
- WooCommerce ships an `is_checkout()` and `is_cart()` conditional you can use to load accessibility-focused JS only on the pages that need it. Putting the focus trap, aria-live wiring, and Escape handlers in a single woocommerce-a11y.js loaded only on cart and checkout keeps performance overhead near zero.
- If you use Astra Pro, Flatsome, or Kadence, audit the theme's WooCommerce overrides under /woocommerce/ in the theme folder. Theme authors frequently fork the default templates and strip the visible labels, role attributes, or aria-live regions. Re-add the missing attributes via theme child overrides rather than disabling the theme.
- The Stripe and PayPal payment block iframes are out of your control accessibility-wise. Instead of trying to patch them, replace the deprecated 'classic' checkout with the WooCommerce Checkout block (added in WooCommerce 8.3) which provides better focus management around the iframes.
- Run a real shopper journey at least once per quarter with VoiceOver (Mac) or NVDA (Windows). Add a product to cart, change the quantity, apply a coupon, complete the checkout. Many issues only surface on the multi-step transition, not on a single-page audit.
- WooCommerce 8.5 added native support for 'High-Performance Order Storage' (HPOS). It does not affect storefront accessibility but it does change how some accessibility audit plugins (like Accessibility Checker for WooCommerce) report on draft orders. If you use such a plugin, verify HPOS compatibility before enabling.
- The 'Order received' thank-you page is the most-skipped page in accessibility audits because it only renders after a real purchase. Test it by setting WooCommerce to 'Cash on delivery' temporarily, placing a $0.01 test order, and auditing the thank-you page on the live site.
Recommended Tools
Accessibility Checker for WooCommerce by Equalize Digital
WordPress plugin that scans every product, page, and post for WCAG violations directly in the editor. The Pro tier adds full-site scans including the cart, checkout, and My Account templates. Free tier covers single-page audits.
axe DevTools
Browser extension you run on your live cart, checkout, and product pages. Catches dynamic AJAX issues that server-side scanners miss, including the variation swatch focus loss and the side-cart drawer focus management.
WAVE browser extension
Visual scanner that highlights missing labels, contrast issues, and ARIA errors directly on the page. Useful when explaining specific issues to a non-technical store owner during a handover.
Pa11y CI
Command-line scanner you can wire into your deploy pipeline to fail builds when a checkout-flow URL regresses. Works well with WooCommerce because it follows redirects after a test order.
NVDA screen reader
Free Windows screen reader that exposes the most demanding accessibility behaviour. Walking the cart-to-checkout flow with NVDA + Firefox catches issues that VoiceOver hides through Apple-specific heuristics.
Further Reading
- Woocommerce Accessibility Six Failures
- Accessible Ecommerce Checkout Guide
- Shopify Store Accessibility Guide
Other CMS Checklists
Get our free accessibility toolkit
We're building a simple accessibility checker for non-developers. Join the waitlist for early access and a free EAA compliance checklist.
No spam. Unsubscribe anytime.