How to Fix Shopify Checkout Keyboard Navigation

Cart drawer, cart page, express checkout and checkout — how to test each with a keyboard, and the code fix for every failure point.

July 2026 18 min read Guide
Shopify checkout page with keyboard accessibility violations marked: form fields relying on placeholders instead of labels, low contrast text, missing focus indicators and keyboard traps

Your Shopify checkout probably isn't keyboard accessible.

A customer with a motor disability tries to checkout. They use keyboard only (no mouse). They Tab through the checkout form. Then they get stuck. They can't proceed to payment. They can't complete the purchase.

You just lost a sale.

This violates WCAG 2.1.1 (Keyboard). It's also costing you money in cart abandonment.

This guide covers the full path to checkout, not just the payment page: the cart drawer, the cart page, express checkout buttons, and Shopify's checkout itself. Here's exactly how to test each one and how to fix what you find.

What you'll be able to do after this guide

  • Run a complete keyboard test on your own checkout in about 15 minutes
  • Identify which of the 9 common failure points your store has
  • Apply the specific code fix for each one
  • Verify the fix with a screen reader, not just by eye
  • Know which parts of checkout you can edit and which you can't

First: What You Can Actually Edit in Shopify Checkout

This trips up almost everyone, so it's worth settling before you start changing code.

Shopify's checkout page itself is locked down. On every plan except Shopify Plus, you cannot edit checkout.liquid. Shopify controls that markup, and it is largely accessible out of the box.

What you can and cannot edit in the Shopify checkout path
Area Can you edit it? Where the fix lives
Cart drawer / mini cartYesTheme Liquid + JavaScript
Cart pageYescart.liquid or main-cart.liquid
Express checkout buttonsPartlyTheme markup around the button
Checkout page (non-Plus)NoShopify controls this
Checkout page (Plus)Yescheckout.liquid
Checkout extensions / appsYesRemove, replace, or ask the developer

So why is your checkout broken? In most cases the failure isn't on Shopify's checkout page at all. It's in the theme code and apps that sit before it — the cart drawer that traps focus, the express checkout button with no accessible name, the upsell modal that appears on the cart page. Customers never reach Shopify's accessible checkout because they get stuck on the way there.

Why Shopify Checkout Has Keyboard Navigation Issues

Shopify's checkout is mostly accessible by default. BUT: Many merchants customize their checkout with custom code and third-party apps. These customizations often break keyboard accessibility.

Problem 1: Custom Payment Buttons Not Keyboard Accessible

Store owner adds custom CSS to style payment button. The custom CSS hides the default focus state. Keyboard users can't see where they are.

Problem 2: Form Fields Missing Labels

Custom checkout code adds form fields without proper labels. Screen readers and keyboard users don't know what to enter.

Problem 3: Apps Breaking Checkout Flow

Payment processor integrations, upsell apps, and discount apps often break keyboard navigation. Keyboard users get stuck and can't proceed.

Problem 4: Focus Trap Issues

Custom modals in checkout trap keyboard focus. Users can't escape with Escape key. They're stuck in a field/modal.

Problem 5: The Cart Drawer Never Receives Focus

This is the single most common failure we find. The customer activates the cart button, the drawer slides open visually, but keyboard focus stays behind it on the page. The user Tabs forward and moves through the hidden page underneath, never reaching the checkout button inside the drawer.

The 15-Minute Keyboard Test (Full Protocol)

Run this on your live store. You need no tools beyond a keyboard.

Before you start: if you use Safari on Mac, keyboard navigation is off by default. Turn it on in Settings → Advanced → Press Tab to highlight each item, or the test will produce false failures.

Stage 1: Product page to cart

  1. Put your mouse away. Physically move it if you have to.
  2. Open a product page and press Tab repeatedly until you reach the variant selectors.
  3. Select a size or colour using Arrow keys. Radio groups should respond to arrows, not Tab.
  4. Tab to Add to cart and press Enter.

Watch for: did anything announce that the item was added? If a cart drawer opened, keep reading — stage 2 is where most stores fail.

Stage 2: The cart drawer

  1. With the drawer open, press Tab once.
  2. Where did focus go? It should land inside the drawer, usually on the close button.
  3. Tab through the drawer contents: quantity steppers, remove buttons, checkout button.
  4. Press Escape. The drawer should close.
  5. After closing, press Tab. Focus should return to the cart button you started from.

Failure signs: focus jumps to the page behind the drawer, Escape does nothing, or after closing you find yourself back at the top of the page with no idea where you are. Any of these is a WCAG 2.1.2 or 2.4.3 failure.

Stage 3: The cart page

  1. Navigate to /cart directly.
  2. Tab to the quantity field. Can you change quantity with the keyboard?
  3. If quantity uses plus and minus buttons, do they have names? A bare + announces as "button" and nothing else.
  4. Tab to Remove. If you have three items, do all three remove links say the same thing? "Remove" three times gives no indication of which item.
  5. Update the quantity. Did the subtotal change get announced, or did it change silently?

Stage 4: Express checkout buttons

Shop Pay, PayPal, Google Pay and Apple Pay buttons sit above the standard checkout button on most themes.

  1. Tab to each express button in turn.
  2. Is there a visible focus indicator on each one?
  3. Do you know which is which without looking? Many render as an image or icon with no text alternative.
  4. Press Enter on one. Does it open, and can you Escape back out?

Stage 5: Shopify checkout

  1. Tab through: Email → Name → Address → Shipping → Payment.
  2. Fill each field by Tab and typing.
  3. Select a shipping method with Arrow keys.
  4. Trigger a validation error deliberately — enter an invalid email and continue. Was the error announced, and did focus move to it?
  5. Complete the order with Enter.

Record as you go. Write down the exact point where you get stuck, with the page URL and the element. "I can't Tab to the payment button" is actionable. "Checkout is broken" is not. If you ever receive a demand letter, this record is the start of your documentation.

Verify With a Screen Reader (Not Just Your Eyes)

Keyboard testing tells you whether a control can be reached. It doesn't tell you whether the control makes any sense when announced. A button you can Tab to that announces as "button, blank" is still unusable.

NVDA on Windows

  • Free from nvaccess.org
  • Start and stop: Ctrl + Alt + N
  • Silence speech: Ctrl
  • List all form fields: NVDA + F7

VoiceOver on Mac

  • Built in, no install needed
  • Start and stop: Cmd + F5
  • Move: Ctrl + Option + Arrow
  • Open the rotor: Ctrl + Option + U

What good sounds like as you Tab through a cart drawer:

expected announcements
"Your cart, dialog" "Close cart, button" "Aurora Silk Midi Dress, size Medium, quantity 1" "Decrease quantity of Aurora Silk Midi Dress, button" "Remove Aurora Silk Midi Dress from cart, button" "Subtotal, 128 US dollars" "Checkout, button"

What broken sounds like on the same drawer:

common failure output
"dialog" "button" "clickable" "button" "button" "one two eight" "clickable"

Same page. Same visual design. Completely different experience.

The Most Common Checkout Keyboard Issues (And Fixes)

Issue #1: Form Fields Without Labels

What it looks like:

form labels
<!-- BAD - No label --> <input type="email" placeholder="Enter your email"> <!-- GOOD - Has label --> <label for="email">Email Address</label> <input type="email" id="email">

Impact: Keyboard users can't see what field they're in. Screen readers say nothing. User gets confused.

Placeholders are not labels. Placeholder text disappears the moment the user starts typing, which removes the only cue about what the field was for. It also typically fails contrast at around 2.5:1. This is WCAG 1.3.1 and 3.3.2, and it is the most frequently cited violation in checkout-related complaints.

Issue #2: Payment Button Not Keyboard Accessible

What it looks like: Payment button only works with mouse click. Keyboard users press Tab but button doesn't activate with Enter.

The usual cause is a <div> styled to look like a button. A div is not focusable and does not respond to Enter or Space.

payment button
<!-- BAD - div pretending to be a button --> <div class="pay-btn" onclick="processPayment()">Pay Now</div> <!-- GOOD - a real button, keyboard support for free --> <button type="button" onclick="processPayment()"> Pay Now </button>

The rule that prevents most of these bugs: use the real HTML element. A native <button> is focusable, responds to Enter and Space, announces as a button, and works with voice control — none of which you have to write. Reaching for a div and adding handlers back on is how checkout breaks.

Issue #3: Shipping Method Selection Not Keyboard Accessible

How to fix: Use native HTML radio buttons for automatic keyboard support.

shipping options
<fieldset> <legend>Shipping method</legend> <label> <input type="radio" name="shipping" value="standard" required> Standard Shipping (5-7 days) — Free </label> <label> <input type="radio" name="shipping" value="express" required> Express Shipping (2-3 days) — $12.00 </label> </fieldset>

The <fieldset> and <legend> matter: without them a screen reader announces "Standard Shipping, radio button" with no indication of what question is being answered.

Need professional help?

Can't fix keyboard navigation yourself? Our accessibility experts can audit your entire checkout and implement all fixes for WCAG compliance.

Get your free audit →

Issue #4: Discount/Coupon Code Input Not Keyboard Accessible

How to fix: Add label to coupon field and ensure the result of applying it is announced.

discount code
<label for="coupon">Discount code (optional)</label> <input type="text" id="coupon" name="coupon"> <button type="button" onclick="applyCoupon()">Apply code</button> <!-- The result must be announced, not just shown --> <p id="couponStatus" role="status"></p> <script> function applyCoupon() { // ... apply the code, then: document.getElementById('couponStatus').textContent = 'Code SAVE10 applied. 10% discount, new total $115.20.'; } </script>

Issue #5: Billing Address Same as Shipping Checkbox

How to fix: Use native HTML checkbox with proper label.

billing checkbox
<label> <input type="checkbox" id="sameBilling" name="sameBilling"> Billing address same as shipping </label>

If ticking this box reveals or hides a block of address fields, that change also needs announcing — add aria-expanded to the checkbox and aria-controls pointing at the fields.

Issue #6: Submit Button Not Clearly Accessible

How to fix: Ensure submit button is always visible, reachable, and keyboard-accessible.

submit button
<button type="submit" class="btn-primary"> Complete Order </button>

Issue #7: Cart Drawer Focus Management

The big one. When the drawer opens, focus must move into it. While it's open, Tab must stay inside it. When it closes, focus must return to where it came from.

cart drawer · complete focus handling
const cartBtn = document.getElementById('cart-button'); const drawer = document.getElementById('cart-drawer'); const closeBtn = drawer.querySelector('.drawer-close'); function openDrawer() { drawer.classList.add('is-open'); drawer.setAttribute('aria-hidden', 'false'); closeBtn.focus(); // 1. move focus IN document.addEventListener('keydown', onKeydown); } function closeDrawer() { drawer.classList.remove('is-open'); drawer.setAttribute('aria-hidden', 'true'); cartBtn.focus(); // 3. send focus BACK document.removeEventListener('keydown', onKeydown); } function onKeydown(e) { if (e.key === 'Escape') { closeDrawer(); return; } if (e.key !== 'Tab') return; // 2. keep Tab INSIDE the drawer const items = drawer.querySelectorAll( 'a[href], button:not([disabled]), input, select, textarea' ); if (!items.length) return; const first = items[0]; const last = items[items.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } cartBtn.addEventListener('click', openDrawer); closeBtn.addEventListener('click', closeDrawer);

Mark the drawer up as a dialog so screen readers announce it as one:

drawer markup
<div id="cart-drawer" role="dialog" aria-modal="true" aria-labelledby="cartHeading" aria-hidden="true"> <h2 id="cartHeading">Your cart</h2> <button class="drawer-close" aria-label="Close cart">&times;</button> <!-- items --> </div>

Issue #8: Cart Actions With Ambiguous Names

Three items in the cart, three buttons that all announce as "Remove". The customer has no way to know which one they're about to activate.

cart item controls
<!-- BAD - identical, meaningless names --> <button>&minus;</button> <button>+</button> <a href="/cart/change?id=123&quantity=0">Remove</a> <!-- GOOD - names include the product --> <button aria-label="Decrease quantity of Aurora Silk Midi Dress">&minus;</button> <button aria-label="Increase quantity of Aurora Silk Midi Dress">+</button> <a href="/cart/change?id=123&quantity=0"> Remove Aurora Silk Midi Dress from cart </a>

In Liquid, build the name from the line item so it stays correct as the cart changes:

liquid
<button aria-label="Decrease quantity of {{ item.product.title }}"> &minus; </button>

Issue #9: Cart Updates Happen Silently

The customer removes an item. The subtotal changes. Visually it's obvious. To a screen reader user, nothing happened at all.

live region for cart updates
<!-- Sits in the page, empty, ready to announce --> <p id="cartStatus" role="status" aria-live="polite" class="sr-only"></p> <script> function announce(message) { document.getElementById('cartStatus').textContent = message; } // After a successful cart update: announce('Aurora Silk Midi Dress removed. Cart now has 2 items, subtotal $96.00.'); </script>

The live region must already exist in the DOM when the page loads. Injecting the element and its text at the same moment usually produces no announcement at all — assistive technology needs the container present in order to watch it.

Never Remove the Focus Outline

The most damaging single line of CSS in ecommerce:

do not do this
/* Removes every focus indicator on the site */ *:focus { outline: none; }

Designers remove it because the default ring looks untidy. The result is a checkout a keyboard user can technically operate but cannot see. They're Tabbing blind.

If you don't like the default, replace it — don't delete it.

a focus ring worth keeping
/* Applies only to keyboard focus, not mouse clicks */ :focus-visible { outline: 3px solid #175BFF; outline-offset: 2px; border-radius: 3px; } /* On dark backgrounds, add a light halo so it stays visible */ .dark-section :focus-visible { outline-color: #fff; box-shadow: 0 0 0 5px rgba(23,91,255,.6); }

WCAG 2.2 raised the bar here with success criterion 2.4.11 (Focus Not Obscured) — the focused element must not be hidden behind a sticky header, cookie banner or chat widget. Worth checking, since sticky headers are near-universal on Shopify themes.

Express Checkout Buttons (Shop Pay, PayPal, Apple Pay)

These sit at the top of most carts and are frequently the first thing a keyboard user reaches. They're also frequently unlabelled, because the button content is an image or a brand logo.

express buttons
<!-- BAD - logo only, announces as "button" --> <button class="shop-pay"><img src="shop-pay.svg"></button> <!-- GOOD - the image carries the name --> <button class="shop-pay"> <img src="shop-pay.svg" alt="Check out with Shop Pay"> </button> <!-- Also fine - name on the button, image decorative --> <button class="shop-pay" aria-label="Check out with Shop Pay"> <img src="shop-pay.svg" alt=""> </button>

Group them so their purpose is clear, and label the alternative path too:

grouping
<div role="group" aria-label="Express checkout options"> <!-- express buttons --> </div> <p id="orLabel">Or continue to standard checkout</p>

Step-by-Step: Fix Your Checkout Keyboard Navigation

Step 1: Identify the Exact Problem

Test your checkout with keyboard only. Note down EXACTLY where you get stuck. Example: "I can't Tab to the payment button"

Step 2: Duplicate Your Theme First

Shopify Admin → Online Store → Themes → Actions → Duplicate. Work on the copy. If a change breaks the cart, your live store is untouched. Skipping this step is how a small accessibility fix turns into an outage.

Step 3: Find the Code

Shopify Admin → Sales Channels → Online Store → Themes → Edit Code. Cart drawer markup usually lives in snippets/cart-drawer.liquid or sections/cart-drawer.liquid. Cart page markup is in sections/main-cart-items.liquid. Drawer behaviour is typically in assets/global.js or a theme-specific JS file.

Step 4: Make the Fix

Use the code examples above that match your problem. Change one thing at a time so you know what fixed what.

Step 5: Test Again

Refresh your store. Run the full keyboard protocol again from stage 1. Does it work now?

Step 6: Test on Mobile

Shopify checkout also needs to work on mobile. Connect a Bluetooth keyboard to your phone and repeat the test, and check touch targets are at least 44×44px while you're there. Mobile is over half your traffic.

Step 7: Publish and Document

Publish the corrected theme, then record what you changed and when. That record is what turns a fix into a legal defence.

Verification Checklist

Cart drawer

  • Focus moves into the drawer when it opens
  • Tab stays inside while it's open
  • Escape closes it
  • Focus returns to the cart button on close
  • Announced as a dialog with a name

Cart page

  • Quantity controls have product-specific names
  • Remove links identify which item
  • Subtotal changes are announced
  • Empty cart state is announced

Checkout

  • Can Tab through all checkout fields
  • All fields have visible labels
  • Can complete entire checkout with keyboard only
  • Can submit order with Enter key
  • Shipping options work with Arrow keys
  • Discount code can be applied by keyboard
  • Errors are announced and receive focus

Everywhere

  • Focus always visible
  • Focus never hidden behind a sticky header
  • Express checkout buttons have names
  • Tested with NVDA or VoiceOver
  • Tested on desktop browser
  • Tested on mobile device

How Fixing This Impacts Your Business

Legal Impact

Fixing keyboard navigation removes a WCAG 2.1.1 violation. Checkout barriers are the most damaging category in an ADA complaint, because they block the transaction itself rather than merely making browsing harder — the discrimination is direct and easy to demonstrate in court.

Conversion Impact

Disabled customers can now checkout. Keyboard users no longer abandon carts. Anyone whose mouse has failed, who is navigating one-handed, or who simply prefers the keyboard also completes checkout instead of leaving.

User Experience

Everyone benefits from better keyboard navigation. Mac users with accessibility preferences appreciate it. Power users prefer keyboard navigation. And the same semantic markup that makes checkout accessible also makes it more robust to theme updates.

Need Professional Help?

If you can't find your checkout code, don't understand the fixes, want guaranteed compliance, or have complex custom checkout, our Shopify checkout accessibility service covers the whole path: cart drawer, cart page, express checkout and checkout extensions, tested with real assistive technology and documented for your records.

Find out exactly where your checkout breaks

We run the full keyboard and screen reader protocol on your store and send back a recorded walkthrough showing precisely where a customer would get stuck.

Get your free checkout audit →

Summary

Shopify checkout keyboard navigation is critical. Without it, disabled customers can't buy. You're losing revenue AND violating ADA.

Most of the time the problem isn't Shopify's checkout page — it's the cart drawer, the express buttons and the apps sitting in front of it. Those you control, and those you can fix.

Run the 15-minute test. Note where you get stuck. Apply the matching fix above. Verify with a screen reader. Then document what you did.

If you can't fix it yourself, we can. But fix it now. Don't wait for a demand letter to make the decision for you.

Free 5-Email Accessibility Course

Get actionable tips on what's blocking your customers and costing you revenue. Plus, a free accessibility audit of your store.

Something went wrong. Please check your email and try again.
Perfect! Check your email for lesson 1. It's on the way.

Free Shopify Accessibility Tips

Get our 5-email course. Plus a free accessibility audit.