Sample accessibility audit: full report
A WCAG 2.2 AA audit of a live EU online shop: 20 findings ranked by how much they hurt, and a draft accessibility statement.
1. Cover
| Site | A mid-size EU online shop that sells building materials. Online shop with a single page checkout, card payment, a cookie consent module and an accessibility menu widget. |
|---|---|
| Date | October 26, 2026 |
| Standard | WCAG 2.2 level AA (covers WCAG 2.1 AA, which EN 301 549 points to) |
| Pages tested | 15, each at desktop 1440 px and phone 390 px width (list below) |
| Tools | axe-core 4.11.4 (rule sets wcag2a, wcag2aa, wcag21a, wcag21aa, wcag22aa) run in Playwright 1.60 with Chromium 148. Manual keyboard testing driven through Playwright (Tab, Shift+Tab, Enter, Space, Escape, arrow keys). Focus visibility checked by comparing screenshots of each control with and without focus. Code inspection of names, roles, labels and live regions. Reflow checked at 320 px. No Lighthouse, no screen reader (see section 2). |
| Tested by | Matjaž Korošec (koroCode) |
This is a sample. The shop is live and public; its name, address, brands and products are removed, and the interface text is translated into English. Your report stays private: samples use public sites with names removed. The paid report also gives the exact address of every page and the exact element for every finding.
Pages tested
- Home
- Search results
- Category overview
- Product listing
- Product pages, 2: 1 with a required option (length), 1 without
- "Added to cart" popup
- Cart, with 1 product added as a guest
- Checkout, one page, viewed only, nothing typed
- Login
- Registration, viewed only
- Contact
- Help form (modal on every page)
- Cookie banner
- Accessibility statement
- Delivery and payment
Plus a check of the option picker on 21 product pages from 4 categories (stone wool insulation, ceiling roses, hand tools, adhesives) and the home page.
No form was submitted, no account created, no personal data entered, no order placed. One product was added to the cart as a guest.
2. Summary for the boss
Status: not compliant. A keyboard user can't buy more than 1 in 4 of the products I checked.
The site has a good base: the language is set, every page has a clear title and heading, the skip links work, product images have descriptions, the checkout fields have labels, the help form traps focus correctly, and the page reflows at 320 px with no sideways scrolling. Zooming on phones works. The shop already publishes an accessibility statement from late 2025, based on a self check with automated tools. It lists alt text and PDFs as open items and gives no conformance status. It does not mention any of the problems below. The problems sit in a few shared templates, so each fix lands on every page at once.
The 5 worst problems, in plain words
- Products with a choice can't be bought by keyboard. When a product has a required choice (length, thickness, pack size, board size), the choice buttons are hidden from the keyboard and from screen readers. "Add to cart" then fails with "Warning: Length field required!" 6 of 21 products checked.
- Login, registration and contact fields lose their labels. The hidden help form uses the same field ids, so screen readers announce the visible e-mail, name, phone, city and message fields with no label.
- Add to cart, compare and wishlist on category and search pages can't be reached by keyboard. They are links with no address. A keyboard user has to open every product page instead.
- On phones, the product menu can't be opened by keyboard. "Product categories" is a plain box, not a button, so it gets no focus and screen readers don't know it opens a menu.
- You can't see where you are. Most links and buttons, including add to cart, the cart, the main menu and the cookie banner's accept button, show no change at all when they get keyboard focus.
Fix effort: medium. About 8 to 12 developer days, mostly template work in the shop theme, plus the cookie module.
Price for the fixes: about 4,000 to 6,000 EUR, at the low end of my 3,000 to 12,000 EUR range. Why: there is 1 blocker and it is a small CSS and markup change; the rest repeats through a handful of shared templates (product options, product card, header menu, popups, form ids) rather than across hundreds of custom pages; no redesign is needed. It is not at the very bottom because the cookie banner and the checkout come from third party modules, which take longer to change safely.
Screen reader pass: not part of this sample. I did not run a screen reader for this sample. Every screen reader point in the table is read from the code, and marked "from code". A paid audit checks all of them by hand:
- NVDA with Firefox or Chrome on Windows, VoiceOver with Safari on Mac, VoiceOver on iPhone.
- Product page: is the length choice announced at all (expected: no). Does anything announce the "length is required" error after add to cart (expected: no).
- Login, registration and contact: what the e-mail, name, phone, city and message fields announce (expected: "edit text" with no name).
- "Added to cart" popup: is it announced as a dialog, with a name, when it opens. Where focus lands, and where it goes after closing.
- Cart button in the header: does its name plus the price make sense.
- Checkout: are required fields announced as required (the asterisk is CSS only). Are the 2 password fields announced with a name. Shipping and payment radio groups: is the group question read.
- Search: are the suggestions announced when they appear, and their count.
- Cookie banner: is it announced on load, and can you reach the accept button quickly.
- Phone menu with VoiceOver on iPhone: does it say "button" and "collapsed/expanded".
- Side tabs for help, the accessibility menu and cookies: what each one announces.
- Category sidebar +/- toggles: what they announce.
3. How I checked
Every finding below was seen on the live site, at both widths, and is backed by a screenshot or a saved test result. Nothing was typed into a form and nothing was sent.
| Area | What I checked | How |
|---|---|---|
| Automated rules | Every WCAG 2.0, 2.1 and 2.2 A and AA rule that axe can test | axe-core 4.11.4 on 12 page states at 1440 px and 390 px (24 scans), plus 5 states that only appear after a click (cookie banner, help form, "added to cart" popup, full cart, checkout) at both widths. |
| Keyboard | Can every task be done with Tab, Shift+Tab, Enter, Space, Escape and arrow keys | Scripted keyboard walks through home, listing, product, cart and checkout, logging every stop and the element that had focus. |
| Buying | Can a keyboard user put a product in the cart | Tried the option picker on 21 product pages from 4 categories and the home page, and counted how many had a required choice. |
| Focus | Can you see which control is active | Screenshot of 12 typical controls with and without focus, compared pixel by pixel. |
| Names and labels | What each field, button and link is called for a screen reader | Read each field's labels list and accessible name from the browser, and compared it with the visible text. |
| Popups | Help form, "added to cart" popup, cookie banner | Opened each one, checked its role and name, where focus lands, whether focus stays inside, and where it goes after Escape. |
| Phone | Menu, header, banner and popups at 390 px | The same keyboard walks at phone width, plus the space the cookie banner takes on an 844 px tall screen. |
| Reflow and zoom | Does the page work at 320 px and can you pinch zoom | Loaded 6 pages at 320 px and measured sideways scroll; read the viewport tag on desktop and on a phone user agent. |
| Contrast | Is the text dark enough on its background | axe measurements, plus spot checks on footer links, card prices and the cookie text. |
| The current statement | What the shop already says about accessibility | Read the published statement and compared its open items with what I found. |
Severity. Blocker: a task can't be done at all. High: a main task is very hard, or a group of users loses information they need. Medium: harder or slower, with a work-around. Low: an annoyance, or a problem for few users.
Effort. S: under half a day for a developer who knows the theme. M: about 1 to 2 days.
4. Findings at a glance
| # | Where | What fails | Who it blocks | WCAG | Severity | Fix | Effort |
|---|---|---|---|---|---|---|---|
| 1 | Product pages with a required choice: 6 of 21 checked (length, pack size, thickness, board size) | The choice is radio inputs set to visibility: hidden with styled labels on top. Tab skips them; they can't take focus even from script. Add to cart then shows "Warning: Length field required!" and nothing is added. Hidden inputs are also dropped from the accessibility tree (from code). | Keyboard users, switch users, screen reader users | 2.1.1, 4.1.2 | Blocker | Hide the inputs with a visually hidden class (clip), not visibility: hidden. Show a focus ring on the label when its input has focus. Wrap each group in a fieldset with the option name as legend. | S |
| 2 | Same product pages | After a failed add to cart, the error appears as plain text with no role="alert" or live region, and focus drops to the page body. Radios get no aria-invalid. | Screen reader users, keyboard users (from code) | 3.3.1, 4.1.3 | High | Put the error in a role="alert" region, move focus to the option group, link the error with aria-describedby. | S |
| 3 | Whole site | No visible focus on most controls. Screenshot comparison, focused vs not: no change at all on 7 of 12 controls tested (social links, login link, cart button, main menu items, help tab, footer links, add to cart). The cookie accept button has outline: none. Search and quantity fields only darken the border. | Keyboard users, low vision users | 2.4.7 | High | 1 global :focus-visible style: a 2 px outline with 3:1 contrast against both the white and the blue backgrounds. | S |
| 4 | Category listings and search results: every product card (14 on the listing tested, 20 on search) | Add to cart, compare and wishlist are <a> with onclick and no href. Tab never reaches them (115 Tab presses through the listing page, 0 stops on them). | Keyboard users, switch users | 2.1.1 | High | Make them <button type="button">. | S |
| 5 | Phone width (390 px), header on every page | The "Product categories" menu toggle is a div. Tab skips it and the menu stays shut. No button role, no aria-expanded. The "skip to main navigation" link points at the closed menu. | Keyboard users on phone and tablet, switch users, screen reader users (from code) | 2.1.1, 4.1.2 | High | Make it a <button> with aria-expanded and aria-controls. | S |
| 6 | Login, registration, contact | The hidden help form on every page uses the same ids as these forms (e-mail, name, telephone, city, message). The browser ties each label for to the first id, the hidden one. Fields with no label: login e-mail; registration e-mail, telephone, city; contact name, e-mail, city, message (checked through each field's labels list). | Screen reader users, voice control users | 1.3.1, 4.1.2 | High | Give the modal fields their own ids and update its labels. | S |
| 7 | Desktop main menu, every page | Subcategory panels open on mouse hover only. When a top item has keyboard focus the panel stays visibility: hidden, and Tab goes on to the next top item. No aria-haspopup or aria-expanded. Work-around: open the category page. | Keyboard users | 2.1.1 | Medium | Open the panel on focus and on Enter or Arrow Down, close on Escape, add aria-expanded. | M |
| 8 | Cookie banner and side tabs, every page | The link that opens cookie settings, the cookie side tab and the accessibility menu side tab are <a> with no href. None gets keyboard focus. A keyboard user can accept cookies but can't open the settings, and can't open the accessibility menu. | Keyboard users | 2.1.1 | Medium | Make them <button type="button">. | S |
| 9 | "Added to cart" popup; help form | Popup has no role="dialog", no name; focus goes to a wrapper div. On Escape, focus falls to the page body in both the popup and the help form, so the user starts again at the top. | Keyboard and screen reader users | 4.1.2, 2.4.3 | Medium | Add role="dialog", aria-modal="true" and aria-labelledby to the popup. On close, return focus to the button that opened it. | M |
| 10 | Checkout, registration, login, contact | No autocomplete on name, e-mail, phone, address and postcode fields. Login and checkout e-mail fields are type="text". | People with memory, motor or reading difficulties who rely on autofill | 1.3.5 | Medium | Add autocomplete values (given-name, family-name, email, tel, street-address, address-level2, postal-code, country) and type="email". | S |
| 11 | Checkout, "create account" part | 2 password fields with no label (axe, desktop and phone). | Screen reader users | 1.3.1, 4.1.2 | Medium | Add label for with the visible text, or aria-label. | S |
| 12 | Checkout, registration, contact, help form | Required fields are marked by a red asterisk added with CSS ::before only. No required or aria-required on any field. | Screen reader users (from code) | 1.3.1, 3.3.2 | Medium | Add required (or aria-required="true") to each required field. | S |
| 13 | Search results | "Sort by" and "Show" selects have no accessible name (axe, 2 selects, desktop and phone). | Screen reader users | 4.1.2 | Medium | Tie the visible text to each select with label for. | S |
| 14 | Cookie banner, every page until accepted | The banner sits on top of everything, also above the help form, whose send button lies under it with only a 2 px edge showing. The accept button is last in the tab order: 71 Tab presses on home, 74 on a product page, 115 on a listing. The banner takes 169 px of an 844 px phone screen. | Keyboard users, screen magnifier users | 2.4.11 (borderline: a 2 px edge stays visible) | Medium | Put the banner first in the page and move focus to it on load, or reserve space for it so it never covers content. | S |
| 15 | Category sidebar | The +/- toggles that open subcategories have no text or name (axe, 2 on the page tested, desktop and phone). | Screen reader users | 4.1.2, 2.4.4 | Low | Add an aria-label with the category name and aria-expanded. | S |
| 16 | Every page | 2 images in the side tabs (accessibility and cookies icons) have no alt (axe, all 24 page scans). | Screen reader users | 1.1.1 | Low | Add a short alt, or alt="" once the tab has its own name. | S |
| 17 | "Added to cart" popup | Quantity +, field and minus are 42 by 20 px with no space between them (axe, 3 targets desktop, 4 phone). | People with tremor, phone users | 2.5.8 | Low | At least 24 by 24 px, or space between. | S |
| 18 | Header, home, product page | Visible text missing from the accessible name: the cart button shows the cart total but is named "open cart"; blog "read more" buttons are named after the article; the wishlist link's name uses different words from its visible text. | Voice control users | 2.5.3 | Low | Start each aria-label with the visible words, or drop the aria-label. | S |
| 19 | Product page | Quantity + and minus are links (href="#") acting as buttons. Enter works, Space does nothing and scrolls the page. | Keyboard users, screen reader users (from code) | 4.1.2 | Low | Make them <button type="button">. | S |
| 20 | Search box, every page | Typing shows a list of 14 suggestions with no combobox roles, no aria-expanded, no announcement. Reachable with Tab, not with arrow keys. | Screen reader users (from code) | 4.1.2, 4.1.3 | Low | Use the combobox pattern, or at least a polite live region with the number of results. | M |
Counts: 1 Blocker, 5 High, 8 Medium, 6 Low. 20 findings.
What axe found on its own: 3 rule failures across 24 page scans (image-alt 48 nodes, select-name 4, link-name 4), plus label 4 and target-size 8 on the popup, cart and checkout. The blocker, the lost labels, the hidden menus and the missing focus were all found by hand. That gap is why a self check with automated tools alone misses them.
5. Every finding in detail
Each finding has where it is, what I saw, who it stops and how, the fix step by step, and how to check that the fix works.
1. Products with a choice can't be added to the cart by keyboard
| Severity | Blocker |
|---|---|
| Effort | S |
| WCAG | 2.1.1 Keyboard, 4.1.2 Name, Role, Value |
| Where | Every product page with a required choice. 6 of the 21 products I checked: 1 with a length, 2 with a pack size, 1 with a thickness, 1 with a board size, and a sanding paper. |
What I found. The choice is built from radio inputs, 1 per value, each set to visibility: hidden. A styled label sits on top of each input and is what you see and click. Hidden this way, the inputs leave the tab order completely: Tab skips every value. They can't take focus even when a script calls focus() on them. Pressing add to cart without a choice shows "Warning: Length field required!", and nothing goes in the cart. visibility: hidden also removes the inputs from the accessibility tree, so a screen reader gets no choice to make (from code).
Who it stops, and how. A customer who can't use a mouse (a hand injury, tremor, a switch device, or simply a keyboard habit) opens a product, picks a quantity, presses add to cart, and gets an error for a field they can't reach. There is no work-around on the page. They leave, or they phone. On these 6 products the shop loses the sale to every keyboard user.
How to fix it.
- Replace
visibility: hiddenon the inputs with a visually hidden class (clip the input to 1 by 1 px, keep it in the page). The inputs then take focus and arrow keys move between values, as with any radio group. - Add a focus style to the label of the focused input, for example
input:focus-visible + label { outline: 2px solid ... }, so the user sees which value is active. - Wrap each group in a
fieldsetwith the option name ("Length") as itslegend, so a screen reader reads the question before the values. - Do it once, in the shared option template: every product with a choice gets the fix.
How to check the fix. On a product with a length choice, Tab lands on the first length. Arrow keys move through the lengths, each with a visible ring. Space picks one, Tab moves on to add to cart, Enter adds the product. Repeat on 1 product from each of the 6.
2. The "required" error is silent
| Severity | High |
|---|---|
| Effort | S |
| WCAG | 3.3.1 Error Identification, 4.1.3 Status Messages |
| Where | The same 6 product pages |
What I found. After add to cart fails, the warning is put into the page as plain text. It has no role="alert" and is not in a live region, so nothing announces it (from code). Focus does not go to the choice: it drops to the page body, so the next Tab starts again at the top of the page. The radios get no aria-invalid, and the warning is not tied to the group with aria-describedby.
Who it stops, and how. Even after finding 1 is fixed, a screen reader user who forgets the choice presses add to cart and hears nothing. As far as they know, the product is in the cart. A keyboard user has to Tab through the whole header again to get back to the choice.
How to fix it.
- Put the warning in an element with
role="alert", so it is read out the moment it appears. - Move focus to the first radio of the group that is missing a value.
- Set
aria-invalid="true"on that group and pointaria-describedbyat the warning. Remove both once a value is picked.
How to check the fix. Press add to cart with no choice made: focus lands on the first value, the warning shows next to it, and a screen reader reads it. Pick a value: the warning goes away.
3. You can't see where keyboard focus is
| Severity | High |
|---|---|
| Effort | S |
| WCAG | 2.4.7 Focus Visible |
| Where | Whole site |
What I found. I took a screenshot of 12 typical controls with and without focus and compared them pixel by pixel. 7 of 12 show no change at all: social links, the login link, the cart button, main menu items, the help tab, footer links and add to cart. The cookie banner's accept button has outline: none. The search field and the quantity field only darken their border, which is hard to see. The skip link and product card links do show focus.
Who it stops, and how. A keyboard user presses Tab and the focus goes somewhere they can't see. They press Enter and something they didn't expect opens, or nothing happens at all. A low vision user using the keyboard with zoom loses their place on every page.
How to fix it.
- Add 1 global rule,
:focus-visible { outline: 2px solid ...; outline-offset: 2px; }, in a color with at least 3:1 contrast against both the white background and the blue header. - Remove the
outline: noneon the cookie accept button and anywhere else the theme or a module sets it. - Use
:focus-visible, not:focus, so mouse users don't see a ring on click.
How to check the fix. Tab through home, a product page and the checkout: every stop shows a clear ring. Repeat the 12 screenshot pairs: every pair differs.
4. Card buttons on category and search pages can't be reached
| Severity | High |
|---|---|
| Effort | S |
| WCAG | 2.1.1 Keyboard |
| Where | Every product card on category listings and search results: 14 cards on the listing tested, 20 on the search tested |
What I found. Add to cart, compare and wishlist on each card are <a> elements with an onclick and no href. A link with no address is not focusable, so Tab never reaches them. I pressed Tab 115 times through the listing page: 0 of those stops landed on any of the 3 buttons. The product name and image links on the card are reachable.
Who it stops, and how. A mouse user adds 5 products to the cart from 1 listing. A keyboard user has to open 5 product pages, and on products with a choice they get stuck there (finding 1). Compare and wishlist can't be used by keyboard at all.
How to fix it.
- In the shared product card template, change the 3 links to
<button type="button">and keep the sameonclick. - Keep the existing classes so the look stays the same; reset the default button styles if needed.
- Give each button a name that includes the product, for example "Add to cart: [product name]", so a screen reader user knows which card they are on.
How to check the fix. Tab through a listing: on each card focus stops on add to cart, compare and wishlist. Enter on add to cart opens the "added to cart" popup.
5. The phone menu can't be opened by keyboard
| Severity | High |
|---|---|
| Effort | S |
| WCAG | 2.1.1 Keyboard, 4.1.2 Name, Role, Value |
| Where | Header on every page at phone width (390 px), also on tablets |
What I found. The "Product categories" toggle that opens the menu on phones is a div with a click handler. It has no tabindex, no button role and no aria-expanded. Tab skips it, so the menu stays shut. The "skip to main navigation" link jumps to the menu, but the menu is closed, so focus lands on nothing useful.
Who it stops, and how. Someone using a phone or tablet with a Bluetooth keyboard or a switch can't browse the categories at all. A screen reader user hears the text but not that it is a button, or whether the menu is open (from code). Search is the only way in.
How to fix it.
- Make the toggle a
<button type="button">with the same text. - Add
aria-expanded="false", switch it totruewhen the menu opens, and pointaria-controlsat the menu. - Let the skip link open the menu, or point it at the button instead of the closed menu.
How to check the fix. At 390 px, Tab reaches "Product categories", Enter opens the menu, Tab moves into it, Escape closes it and focus goes back to the button.
6. Login, registration and contact fields lose their labels
| Severity | High |
|---|---|
| Effort | S |
| WCAG | 1.3.1 Info and Relationships, 4.1.2 Name, Role, Value |
| Where | Login, registration and contact pages |
What I found. The help form is a hidden modal that is in the page on every page. Its fields use the same ids as the shop's own forms: e-mail, name, telephone, city and message. An id must be unique, and when it isn't, the browser ties each label for to the first element with that id, which is the hidden one. I read each visible field's labels list in the browser. Fields with no label: on login, e-mail; on registration, e-mail, telephone and city; on contact, name, e-mail, city and message. axe flags the same thing as duplicate ids on these 3 pages (1, 3 and 4 nodes).
Who it stops, and how. A screen reader user reaches the login form and hears "edit text" instead of "E-mail" (from code). On the contact form 4 of the fields sound the same. A voice control user says "click E-mail" and nothing happens, because the field has no name to match.
How to fix it.
- Give the help form's fields their own ids, for example with a
help-prefix. - Update the help form's
label forvalues to match. - Check any script that reads those fields by id, so the help form still sends.
How to check the fix. On each of the 3 pages, every visible field's labels list holds its visible label. axe shows no duplicate ids. The help form still opens and its fields are still labeled.
7. The desktop main menu opens on hover only
| Severity | Medium |
|---|---|
| Effort | M |
| WCAG | 2.1.1 Keyboard |
| Where | Desktop main menu, every page |
What I found. Each top menu item has a panel of subcategories that opens when the mouse is over it. When a top item has keyboard focus, the panel stays visibility: hidden, and Tab moves on to the next top item. Enter follows the top item's link to the category page. The top items have no aria-haspopup and no aria-expanded.
Who it stops, and how. A keyboard user can't jump straight to a subcategory from the menu. The work-around is to open the category page and pick from there: 1 more page load each time, and the sidebar there has its own problem (finding 15).
How to fix it.
- Open the panel when the top item or anything in its panel has focus (
:focus-within), and on Enter or Arrow Down. - Close it on Escape and return focus to the top item.
- Add
aria-expandedto each top item and keep it in sync with the panel.
How to check the fix. Tab to a top item, press Arrow Down: the panel opens and focus moves into it. Escape closes it. A mouse user sees no change.
8. Cookie settings and the accessibility menu can't be opened by keyboard
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 2.1.1 Keyboard |
| Where | Cookie banner and the side tabs, every page |
What I found. 3 controls are <a> with no href: the link in the cookie banner that opens the cookie settings, the cookie side tab, and the accessibility menu side tab. None of them gets keyboard focus. The accept button is a real button and does work.
Who it stops, and how. A keyboard user can accept all cookies but can't choose which ones, which is the whole point of a consent banner. The accessibility menu, the tool the shop added for people with disabilities, can't be opened by the people most likely to need it without a mouse.
How to fix it. Change the 3 controls to <button type="button">, keeping their classes and handlers. The cookie parts come from a third party module, so check whether its settings allow this, or override its template.
How to check the fix. Tab reaches all 3. Enter opens the cookie settings and the accessibility menu, and focus goes into each.
9. Popups lose your place
| Severity | Medium |
|---|---|
| Effort | M |
| WCAG | 4.1.2 Name, Role, Value, 2.4.3 Focus Order |
| Where | "Added to cart" popup; help form |
What I found. The "added to cart" popup has no role="dialog" and no name. When it opens, focus goes to a wrapper div around it. The help form does have role="dialog" and keeps focus inside, which is good. In both, pressing Escape closes the popup and focus falls to the page body: I logged the focused element after Escape and it was BODY both times, at both widths.
Who it stops, and how. A keyboard user adds a product, closes the popup and is sent back to the top of the page. To add the next product they Tab through the whole header again. A screen reader user isn't told that a dialog opened, or what it is (from code).
How to fix it.
- On the popup, add
role="dialog",aria-modal="true", andaria-labelledbypointing at its heading. - When it opens, move focus to the heading or the first button inside it.
- When either popup closes, return focus to the element that opened it.
How to check the fix. Add a product by keyboard, press Escape: focus is back on the add to cart button. Open the help tab, press Escape: focus is back on the help tab.
10. Forms don't help with autofill
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 1.3.5 Identify Input Purpose |
| Where | Checkout, registration, login, contact |
What I found. I read all 28 fields of the checkout. None has an autocomplete value, and the same is true on registration, login and contact. The e-mail fields on login and checkout are type="text", not type="email".
Who it stops, and how. Autofill is how many people with memory, motor or reading difficulties get through a checkout. Without autocomplete, the browser has to guess, and often fills the wrong field or nothing. On a phone, type="text" also brings up the normal keyboard instead of the one with "@". It slows down every buyer, not only those with a disability.
How to fix it.
- Add
autocompleteto each personal data field:given-name,family-name,email,tel,organization,street-address,address-level2,postal-code,country. Usecurrent-passwordon login andnew-passwordon create account. - Change the e-mail fields to
type="email".
How to check the fix. In a browser with a saved address, clicking the first name field offers the saved data, and choosing it fills every field correctly.
11. Checkout password fields have no label
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 1.3.1 Info and Relationships, 4.1.2 Name, Role, Value |
| Where | Checkout, the "create account" part |
What I found. The 2 password fields (password and repeat password) have visible text next to them, but it isn't tied to them: no id on the field, no label for, no aria-label. axe flags both, at desktop and at phone width.
Who it stops, and how. A screen reader user who wants an account hears "password, edit text" twice, or only "edit text", with no way to tell which is which.
How to fix it. Give each field an id and point the visible text at it with label for, as the other checkout fields already do.
How to check the fix. axe shows no label errors on the checkout, and each password field's labels list holds its visible text.
12. Required fields are marked with a CSS asterisk only
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 1.3.1 Info and Relationships, 3.3.2 Labels or Instructions |
| Where | Checkout, registration, contact, help form |
What I found. Required fields show a red asterisk, added with CSS ::before. No field has required or aria-required: none of the 28 checkout fields has either.
Who it stops, and how. A screen reader doesn't read CSS content reliably, so a blind user can't tell which fields must be filled until they send the form and get errors (from code).
How to fix it.
- Add
required(oraria-required="true") to each required field. - If the form doesn't say it yet, add 1 line above it: "Fields marked * are required."
How to check the fix. Each required field reports required in the browser's accessibility tree; a screen reader says "required".
13. The sort and page size boxes on search have no name
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 4.1.2 Name, Role, Value |
| Where | Search results |
What I found. The "Sort by" and "Show" boxes are <select> elements. The words next to them are plain text, not tied to the box, so the boxes have no name. axe flags both at desktop and at phone width. They also reload the page the moment the value changes, on onchange.
Who it stops, and how. A screen reader user hears "combo box" twice, with no idea what either does. Moving through the values with arrow keys in some browsers reloads the page on the first step, before they reach the value they want.
How to fix it.
- Turn the visible text into
<label for>pointing at each box. - Better: reload only on a separate button or on Enter, not on every change.
How to check the fix. axe shows no select-name errors. A screen reader says "Sort by, combo box".
14. The cookie banner covers things and comes last
| Severity | Medium |
|---|---|
| Effort | S |
| WCAG | 2.4.11 Focus Not Obscured (Minimum), borderline |
| Where | Cookie banner, every page until cookies are accepted |
What I found. The banner sits on top of everything at the bottom of the screen, also on top of the help form. The help form's send button lies under it with only a 2 px edge showing. The accept button is the last thing in the tab order: it takes 71 Tab presses to reach it on home, 74 on a product page and 115 on a listing. On a phone the banner takes 169 px of an 844 px tall screen, a fifth of it.
Who it stops, and how. A keyboard user who wants the banner gone has to Tab through the whole page first, with focus often hidden behind the banner on the way. A screen magnifier user at high zoom sees mostly banner. It counts as borderline for 2.4.11 only because a 2 px edge of the covered button stays visible.
How to fix it. Either:
- Put the banner first in the page and move focus to it on load, so the first Tab is the accept button; or
- Reserve space at the bottom of the page for it, so it never covers content.
How to check the fix. On a fresh visit, the accept button is reached in under 5 Tab presses, and with the banner open the help form's send button is fully visible.
15. Sidebar +/- toggles have no name
| Severity | Low |
|---|---|
| Effort | S |
| WCAG | 4.1.2 Name, Role, Value, 2.4.4 Link Purpose (In Context) |
| Where | Category sidebar |
What I found. The small +/- toggles that open subcategories in the sidebar are links with no text and no name. axe flags 2 on the listing tested, at both widths. They also don't say whether they are open.
Who it stops, and how. A screen reader user hears "link" with nothing after it, once per category.
How to fix it. Add aria-label with the category name, for example "Show subcategories: [category]", and aria-expanded that follows the state.
How to check the fix. axe shows no link-name errors; each toggle reads its category and its state.
16. 2 side tab icons have no alt text
| Severity | Low |
|---|---|
| Effort | S |
| WCAG | 1.1.1 Non-text Content |
| Where | Every page |
What I found. The icons on the accessibility and cookie side tabs are <img> with no alt. axe flags both on every one of the 24 page scans, and in every popup scan.
Who it stops, and how. A screen reader reads the file name of each icon, or "image", on every page.
How to fix it. Once the tabs are buttons with their own names (finding 8), give the icons alt="". Until then, a short alt such as "Accessibility menu" and "Cookie settings".
How to check the fix. axe shows no image-alt errors on any page.
17. Quantity buttons in the popup are too small
| Severity | Low |
|---|---|
| Effort | S |
| WCAG | 2.5.8 Target Size (Minimum) |
| Where | "Added to cart" popup, and the full cart on phones |
What I found. The quantity plus button, the quantity field and the minus button in the popup are each 42 by 20 px, side by side with no space between. axe flags 3 targets at desktop and 4 at phone width, where the remove button joins them. On the phone cart page 1 more target is flagged.
Who it stops, and how. Someone with a tremor, or anyone on a phone, taps minus and gets plus, or removes the product instead.
How to fix it. Make each control at least 24 by 24 px, or keep them the same size and leave enough space between them.
How to check the fix. axe shows no target-size errors on the popup or the cart, at either width.
18. Some names don't match what you see
| Severity | Low |
|---|---|
| Effort | S |
| WCAG | 2.5.3 Label in Name |
| Where | Header, home, product page |
What I found. 3 places where the name a voice control user has to say differs from the visible text. The cart button shows the cart count and total but is named "open cart". The blog "read more" buttons on home are named after the article, without the words "read more". On the product page, the wishlist link's name uses different words from its visible text.
Who it stops, and how. A voice control user says "click read more", or reads the cart total out loud, and nothing happens.
How to fix it. Start each aria-label with the visible words ("Read more: [article]"), or drop the aria-label where the visible text is enough.
How to check the fix. For each of the 3, the accessible name in the browser starts with the visible text.
19. Quantity plus and minus on the product page are fake buttons
| Severity | Low |
|---|---|
| Effort | S |
| WCAG | 4.1.2 Name, Role, Value |
| Where | Product page |
What I found. The quantity plus and minus next to add to cart are links with href="#" that act as buttons. Enter works. Space, which is how a button is pressed, does nothing to the quantity and scrolls the page down instead. A screen reader calls them links (from code).
Who it stops, and how. A keyboard user presses Space on plus and the page jumps. They lose their place, and the quantity stays at 1.
How to fix it. Make them <button type="button"> with names "Increase quantity" and "Decrease quantity", as the popup already does.
How to check the fix. Space on plus raises the quantity by 1 and the page doesn't move.
20. Search suggestions are invisible to screen readers
| Severity | Low |
|---|---|
| Effort | M |
| WCAG | 4.1.2 Name, Role, Value, 4.1.3 Status Messages |
| Where | Search box, every page |
What I found. Typing in the search box opens a list of suggestions, 14 for the word I tried. The box has no combobox role and no aria-expanded, the list has no listbox role, and nothing announces that suggestions appeared or how many (from code). The suggestions can be reached with Tab, not with the arrow keys that people expect.
Who it stops, and how. A screen reader user types, hears nothing, and presses Enter to see the full results page. They never learn the suggestions are there. It is Low because the full search still works.
How to fix it.
- Best: the combobox pattern.
role="combobox"andaria-expandedon the box,role="listbox"on the list,role="option"on each suggestion, arrow keys to move, Enter to pick, Escape to close. - Smaller: a polite live region that says "14 suggestions" when the list opens.
How to check the fix. Type a word: a screen reader says how many suggestions there are. Arrow Down moves into them, Enter opens the one chosen.
6. axe results, page by page
The automated part, for the record. "Failures" are rules axe is sure about. "Needs review" are items axe can't decide on its own; I checked each one by hand, and the real problems among them are in the findings above.
| Page | Desktop failures | Phone failures | Needs review (desktop / phone) |
|---|---|---|---|
| Home | image-alt 2 | image-alt 2 | contrast 15 / 10 |
| Search results | image-alt 2, select-name 2 | image-alt 2, select-name 2 | contrast 15 / 4 |
| Category overview | image-alt 2 | image-alt 2 | contrast 10 / 11, ARIA attribute 1 / 1 |
| Product listing | image-alt 2, link-name 2 | image-alt 2, link-name 2 | contrast 8 / 9 |
| Product page | image-alt 2 | image-alt 2 | contrast 15 / 15, duplicate ids 5 / 5, ARIA attribute 3 / 3 |
| Cart | image-alt 2 | image-alt 2 | contrast 3 / 4 |
| Login | image-alt 2 | image-alt 2 | contrast 7 / 9, duplicate ids 1 / 1 |
| Registration | image-alt 2 | image-alt 2 | contrast 4 / 5, duplicate ids 3 / 3 |
| Contact | image-alt 2 | image-alt 2 | contrast 2 / 3, duplicate ids 4 / 4 |
| Accessibility statement | image-alt 2 | image-alt 2 | contrast 3 / 4 |
| Delivery and payment | image-alt 2 | image-alt 2 | contrast 3 / 4 |
| "Added to cart" popup | image-alt 2, target-size 3 | image-alt 2, target-size 4 | |
| Full cart | image-alt 2 | image-alt 2, target-size 1 | |
| Checkout | image-alt 2, label 2 | image-alt 2, label 2 | contrast 6 on phone, fields with more than 1 label 5 on phone |
| Help form | 1 hidden element that can take focus, both widths | ||
| Cookie banner | contrast 1 / 1 |
Every page also has 1 "link in a text block" item to review: a link told apart from the text around it by color only. It passes where the color contrast between link and text is 3:1 or more; the paid pass measures each one.
What the needs review items turned out to be. The duplicate ids on login, registration and contact are finding 6. On the product page they are the star rating labels of the review form, which reuse the ids of the inputs they label; it doesn't block anything I tested, and is fixed with the same unique id change. The ARIA attribute items are an aria-label on elements that don't allow one, such as the filter button on the category page; they are worth cleaning up, but no user is stopped by them. The contrast items are text over photos and gradients that axe can't measure; see section 7.
7. What is done well
Worth saying, because it is why the price is for fixes and not a rebuild.
| What | Checked how |
|---|---|
| The page language is set on every page | Read the lang value on all 12 pages |
| Every page has a clear, distinct title | Read all 12 titles |
| 1 main heading per page (product pages have 2, which is allowed) | Read the H1s on all 12 pages |
| Skip links are there and work | Tabbed to them on home, product and listing |
| Product images have descriptions | axe and a look at the alt text on listing and product pages |
| The page reflows at 320 px with no sideways scroll | Measured on 6 pages |
| Pinch zoom is allowed on phones | Read the viewport tag on desktop and on a phone user agent |
| The help form keeps focus inside and closes on Escape | Tabbed through it at both widths |
| Checkout fields have labels, apart from the 2 passwords | Read the labels list of all 28 fields |
| Checkout shipping and payment choices have labels | Read the label of each radio |
| The product page quantity field has a label | axe and the labels list |
| Text contrast is strong where axe could measure it | Footer links 7.6:1, card prices 12.6:1, cookie text 11.6:1, against 4.5:1 needed |
8. What I could not check
A sample uses the public site only. A paid audit closes these.
- Screen readers. NVDA and VoiceOver, with the list in section 2. Every "from code" point gets a yes or no.
- Form errors. On checkout, login, registration and contact, the errors only show after sending the form, and I sent nothing.
- Payment and order confirmation. The card payment step and the thank you page need a real order.
- Account pages after login. Order history, addresses, the wishlist page.
- reCAPTCHA in the help and contact forms, and whether there is a way past it without sight.
- Downloadable PDFs. The current statement already lists them as open.
- Text spacing (WCAG 1.4.12) and 200% text zoom on desktop (WCAG 1.4.4).
- Contrast on text over photos and gradients (blog tiles, home banners), which axe can't measure. A paid audit measures each one by hand.
- Wishlist, compare and blog article pages.
9. Draft accessibility statement
Ready for the shop to publish, after translating into its own language. Items in [brackets] are for the shop to fill in.
Accessibility statement for [shop domain]
[Company name] wants everyone to be able to use the online shop at [shop domain], including people who use a keyboard, a screen reader, voice control or zoom. This statement follows [national law], which brings the European Accessibility Act into [country] law.
Conformance status The shop is partially compliant with WCAG 2.2 level AA. Some parts do not meet it yet; they are listed below.
What is not accessible yet
- Some products with a required choice (length, thickness, pack size) can't be added to the cart with a keyboard. Until this is fixed, call us at [phone] or write to [e-mail] and we will take the order for you.
- Keyboard focus is not visible on most links and buttons.
- On category and search pages, the add to cart, compare and wishlist buttons can't be reached with a keyboard. Open the product page to add the product.
- On phones, the product categories menu can't be opened with a keyboard. Use the search or the categories on the home page.
- Some form fields on the login, registration and contact pages are not announced with their names by screen readers.
- The cookie settings and the accessibility menu can't be opened with a keyboard.
- Some PDF documents and embedded maps and videos are not fully accessible.
We plan to fix these by [date]. After the fixes we will have the site checked again and update this statement.
How this statement was prepared The shop was audited on October 26, 2026 by an external tester (koroCode) against WCAG 2.2 AA, with automated tests and manual keyboard tests on 15 pages. This statement was prepared on [date] and will be reviewed at least once a year and after every larger change to the shop.
Report a problem If something on the site doesn't work for you, write to [e-mail for accessibility] or call [phone]. Tell us the page and what went wrong. We reply within [8] working days.
Complaints If you are not happy with our reply, you can turn to the authority that supervises the accessibility of products and services in [country]: [authority name, website, phone].
10. Next step
Fix plan, in order of severity
- Week 1, the blocker and the quick Highs (findings 1 to 6): product option picker, the error message, the global focus style, card buttons, phone menu button, unique ids in the help form. All template and CSS work. After this, a keyboard user can find and buy every product.
- Week 2, the Mediums (7 to 14): keyboard menu, cookie settings and side tabs, popup and dialog focus, autocomplete, checkout labels and required fields, search selects, cookie banner order.
- With the Mediums, the Lows (15 to 20): names, alt text, target size, the search suggestions.
- Screen reader pass with NVDA and VoiceOver on the fixed site.
- Statement: publish the updated statement above with the open items that remain.
Price
| Fixes for all 20 findings | about 4,000 to 6,000 EUR, fixed price after a look at the theme code |
|---|---|
| Full audit of 20 pages with the screen reader pass and draft statement | 2,500 EUR |
| Re-audit after the fixes, so the statement can say "compliant" | 1,200 EUR |
After the re-audit, the statement changes from "partially compliant" to "compliant", with the new audit date.
Matjaž Korošec (koroCode)
Your shop next?
The same audit for your shop, with a statement you can publish. From 2,500 EUR, and you know the price before I start.
Your report stays private. Samples use public sites with names removed.