For auditors: how to write accessibility findings developers can fix fast
What developers need in each accessibility audit finding: location, state, WCAG criterion, steps, expected result and a retest check. CSV template inside.
Short answer
A finding gets fixed fast when a developer can find the code, reproduce the failure and know what "passing" looks like without asking you a question. That takes eight things per finding: a stable ID, the exact location and page state, the component or template it belongs to, the WCAG success criterion, steps to reproduce, the actual result, the expected result, and the check you'll run on retest. Group repeated instances under the component that causes them.
We're on the receiving end of audit reports. Our job is to turn each finding into a code change on a staging theme, then hand it back to the auditor for a retest. This guide is what we wish every report included. It's not a critique of how auditors work. A report written for a client's legal team or leadership has different jobs. But when the same report goes to a developer, a few extra fields cut days off the fix.
The examples come from a 30-finding mock audit we wrote for our demo store (fictional) and then fixed. The full change log shows how each finding mapped to a fix.
Why developers stall on good audits
When a finding is hard to act on, it's rarely about skill. It's usually one of three gaps:
- The finding names the symptom, not the place. "Some buttons have no accessible name" is true and useless at 8 a.m. Which buttons? On which template? In which state?
- The finding describes the failure but not the target. The developer fixes what they think is wrong, and the retest fails on something the auditor expected but never wrote down.
- One root cause is reported 40 times, or 40 causes are reported once. A Shopify theme renders product cards from one snippet. Forty unnamed quick-add buttons are one fix. A "keyboard issues on product page" finding can hide five unrelated fixes.
None of this needs a new methodology. W3C's evaluation methodology, WCAG-EM 2.0 (updated July 2026), and its report template already cover scope, sampling and results. What follows is the developer-facing layer on top.
The eight fields that make a finding fixable
1. A stable ID
KC-014, not "item 14 on page 6". The ID is how the developer's change log, the commit message and your retest notes refer to the same thing. It must not change if you reorder the report.
2. Location and page state
Give the URL or template ("all product pages"), plus the state the bug lives in. Many of the worst failures only exist after an interaction:
- "Cart drawer, open, after adding an item from the product page"
- "Mobile menu at 390 px wide, expanded"
- "Newsletter popup, first visit, private window"
If the state needs data (a sold-out variant, a discount code, a logged-in account), say so. Developers working on a duplicate theme can't guess the product handle you used.
3. The element, in a form a developer can search for
A CSS selector, a short DOM snippet or the visible text of the control. Screenshots help, but a developer searches code, not pixels. Something like .cart-drawer .cart-close or "the × button in the cart drawer header" is enough. If you used a browser extension, the selector it shows is fine.
4. Component or template, and how many instances
This is the field that saves the most time on Shopify and WordPress work. Say whether the issue is one instance, or a pattern across a component:
Component: product card quick-add (home, collection, search results). Instances: every card.
That tells the developer the fix lives in one card snippet, not on 40 pages. It also tells you on retest to sample a few cards on different templates rather than check one.
5. The WCAG success criterion, version and level
Use the number and name, and the version you test against: "4.1.2 Name, Role, Value (A), WCAG 2.2". Link the Understanding document if your team writes for less experienced developers. It explains intent better than any paraphrase.
When a finding fails several criteria, list all of them, but say which one drives the fix. A color swatch built from <div>s can fail 2.1.1, 4.1.2, 1.4.1 and 1.4.11 at once. The fix is a single change (real radio inputs), and the retest checks all four.
6. Steps to reproduce, with the tools you used
Write them the way you tested:
- Open the product page.
- Press Tab from the "128 reviews" link.
- Focus skips the color options and lands on Size.
Add the browser, the assistive technology and its version when the result depends on them ("Safari 26 + VoiceOver, macOS 26"). If an automated rule flagged it, name the rule (button-name, color-contrast). The developer can then run the same rule on their staging copy before handing it back.
7. Actual result and expected result
The actual result is usually in the finding already. The expected result is what's often missing, and it's where most rework comes from. Compare:
Actual: Swatches are not focusable and the selected one isn't announced.
Expected: Color options form a group named "Color". Tab moves into the group, arrow keys change the selection, and a screen reader announces the color name and its selected state. The selected swatch is distinguishable without relying on color alone.
The second version is a spec. It doesn't dictate code, so the developer can choose native radios or an ARIA radio group, but it fixes the behavior you'll test. The WAI-ARIA Authoring Practices Guide is a good shared reference here. "Follow the APG radio group pattern" settles a lot of arguments in one line.
8. The retest check
One or two sentences on how you'll decide pass or fail:
Retest: Tab to Color, use arrow keys; VoiceOver should announce "Slate, selected, radio button, 2 of 3".
This is the field that matters most, because it's the contract. We bill on the partner's retest (if the agreed fixes fail your retest, you don't pay for them), so a written retest check protects both sides. It also stops the "while you're in there" drift where a retest turns into a fresh audit.
Before and after: one finding, rewritten
Here's a finding written the short way, then the same finding with the eight fields. Both describe the cart drawer in our demo store (fictional).
Before
Cart drawer is not accessible for keyboard users. (2.1.1)
After
| Field | Value |
|---|---|
| ID | KC-022 |
| Location / state | All pages; cart drawer open (after Add to cart or the header cart button) |
| Element | .cart-drawer, .cart-close, .cart-line-remove |
| Component / instances | Cart drawer snippet, shared by every page |
| WCAG 2.2 | 2.1.1 Keyboard (A); 2.4.3 Focus Order (A); 4.1.2 Name, Role, Value (A) |
| Steps | 1. Add any product. 2. The drawer slides in; focus stays on Add to cart. 3. Tab: focus moves through the page behind. 4. Escape: nothing. 5. Close drawer, keep tabbing: hidden drawer links still receive focus. |
| Actual | Drawer isn't exposed as a dialog, focus never enters it, × is a <span>, Escape does nothing, closed drawer stays in the tab order. |
| Expected | Modal dialog named "Your cart"; focus moves inside on open; Tab stays inside while open; Escape and a named close button close it; focus returns to the control that opened it; closed drawer is out of the tab order. |
| Retest | Add to cart, Tab around inside the drawer, press Escape, confirm focus is back on Add to cart. Screen reader announces "dialog, Your cart". |
The "after" version takes a few extra minutes to write. It turns a vague ticket into a fix a developer can start without a call, and a retest that takes a minute or two. The fix itself is in our cart drawer guide.
Severity: say what it blocks
Severity scales differ between firms, and that's fine. What helps a developer prioritize is one plain sentence on impact:
- "Blocks keyboard users from completing a purchase" (the popup that can't be dismissed, the drawer you can't leave).
- "Screen reader users can't tell which size is selected."
- "Low-vision users can't read the sale price."
When a client has a limited budget, this sentence is what decides the fix list. It's more useful than "Critical" alone.
Separate what the theme controls from what it doesn't
A report that flags third-party code and platform code clearly saves an awkward conversation at quote time. On Shopify, three kinds of finding look the same in a browser but aren't fixed the same way:
- Theme code: sections, snippets, CSS and JS in the theme. Fixable by a theme developer.
- App code: reviews widgets, popups, chat, upsell drawers. Fixable through app settings, a theme override if the app allows one, a different app, or a ticket to the vendor.
- Checkout: hosted by Shopify on every plan. Checkout is Shopify-hosted on every plan. On non-Plus stores we report checkout issues to Shopify rather than fix them. On Shopify Plus, custom checkout extensions can be in scope. (Details in our Shopify checkout guide.)
You don't need to know the app's name. "Appears to be injected by a reviews app (markup prefixed tp-)" is enough for the developer to check.
Automated and manual findings belong together
Automated scans are a good start and a weak finish. WebAIM notes that automated tools cannot detect every WCAG failure, so a page with no detected errors is not necessarily accessible (WebAIM Million 2026). In our demo audit, axe flagged 13 of the 30 findings at least partly. The other 17 came from keyboard and screen reader testing: focus visibility, the hover-only menu, the carousel with no pause, focus handling in the popup and cart drawer, the silent add-to-cart, and the <div> swatches.
So when a report mixes both, mark how each finding was detected. A developer can verify an axe rule in seconds on staging. A manual finding needs the steps and the expected behavior written out, because there's no rule to rerun.
A findings template you can copy
These are the columns we ask for in a CSV or spreadsheet. They map one-to-one to the fields above, and they import cleanly into most triage sheets. A downloadable version with a one-page guide is on our findings template page.
id,page_or_template,state,component,instances,element,wcag_sc,wcag_version,level,severity,impact,steps,actual,expected,retest_check,detected_by,rule_id,third_party
KC-022,All pages,"Cart drawer open after Add to cart",Cart drawer,"1 (shared snippet)",".cart-drawer, .cart-close","2.1.1; 2.4.3; 4.1.2",2.2,A,Critical,"Keyboard users can't leave the cart","1. Add a product 2. Tab 3. Escape","Focus stays on page behind; Escape does nothing","Modal dialog named Your cart; focus inside; Escape closes; focus returns","Add to cart, Tab inside, Escape, focus back on Add to cart",Manual (keyboard + VoiceOver),,NoIf you already use W3C's WCAG-EM Report Tool or your own template, you don't need to switch. Adding the state, component, instances, expected and retest_check columns is most of the benefit.
What we send back
For every finding, the partner gets one change-log entry: the ID, the success criteria, the files changed, what changed, how we checked it (scan, keyboard steps, screen reader output) and a status that only the partner can move to "Passed retest". Anything we noticed outside the agreed list is written down for the partner to decide on, not quietly fixed. You can see the format in the demo change log, which covers all 30 fictional findings.
That loop (clear finding in, mapped change out, independent retest) is the whole point. The auditor stays the judge. The developer is accountable to a written check, not to someone's memory of a call.
Sources
- W3C WAI, WCAG-EM 2.0: Website Accessibility Conformance Evaluation Methodology (Group Note, July 2026)
- W3C WAI, Template for Accessibility Evaluation Reports and WCAG-EM Report Tool
- W3C WAI, Understanding WCAG 2.2
- W3C WAI, ARIA Authoring Practices Guide: Patterns
- W3C WAI, ACT Rules (shared, informative test procedures, useful when your team and the developer's tools disagree)
- WebAIM, The WebAIM Million (2026)
Have a report with findings like these? Send us one client's audit findings. We review the findings and the code, then quote a fixed price for an agreed list of fixes. Your team retests. If the agreed fixes fail your retest, you don't pay for them. Send findings.
Have a client’s audit to fix?
Send us one client’s audit findings. We quote a fixed price for an agreed list of fixes, fix them on staging with a change log mapped to your issue IDs, and your team retests.