The ticket included a screenshot. The screenshot had a red rectangle drawn around a button. Under that, a rule ID and the word serious.
But here’s the problem, the one engineers experience over and over: No file. No line number. No branch. The component shipped five weeks earlier, and git blame was kind enough to tell me it was mine.
So I did what you did. Grepped for the button text. Found it in three places; two of them dead. Opened the one that wasn’t, stared at it, couldn’t see anything wrong with it, and eventually worked out that the problem wasn’t the button. It had never been a button in the first place.
Total time: about 40 minutes, and the fix took 90 seconds.
The 40 minutes are the part I want to talk about, because none of it was hard. It was all just retrieval. Every single thing I needed to fix that issue existed in one place, at the moment I wrote the code: the component, the intent, the reason it was a div, all of it sitting in my head at the same time. And then we threw that away, waited five weeks, and sent me a picture of it.
Nobody designed the six steps
Draw the path an accessibility issue takes in most companies, and you get something like this: a scanner hits the live site and finds an issue. The finding surfaces in a tool nobody’s editor opens. Somebody writes a ticket. The ticket gets routed, labeled, and prioritized. A developer eventually picks it up and switches over to the repo. They hunt for the code. They fix it. It goes out with the next release.
Six steps. One of them is the fix.
Nobody chose this. It accreted. Each step is a reasonable local response to the step before it: you need a ticket because the finding showed up somewhere the developer isn’t; you need routing because the ticket doesn’t know whose code it is; you need the hunt because the finding points at a rendered pixel and not a line. Every step exists to undo a separation that happened earlier.
Which means you can delete most of them by not separating things in the first place.
So we put the reviewer in the pull request
That’s what accessFlow Code Agent is. It runs on the pull request as you open it, and again on every push, reviews the diff against WCAG 2.2, and when it finds something, it comments inline on the line that caused it, with the change written as a suggested change. You can also connect to Jira Cloud to manage issues within your existing workflow.
You accept it, reject it, or argue with it in the thread. Nothing gets committed to your branch unless a person clicks accept. That last part isn’t a safety blanket we bolted on for its own sake. I’ll come back to why it has to work that way.
The first version was annoying
Our early internal version reported everything it found, at every severity, across every file. Technically correct. Completely unusable. We ran it on our own repos, and within about a week we were doing exactly what you’d do: skimming past the bot, collapsing the thread, merging anyway.
A reviewer you’ve learned to ignore is worse than no reviewer, because now you’ve got the same problems plus a false sense that something’s watching.
So before launch: severity thresholds, so you can start at critical only. Path exclusions so legacy/, vendor/, and your test fixtures don’t generate noise you’ll never action. Manual-only mode, if you want to invoke it rather than have it run. Optional merge gating is off by default because deciding whether accessibility blocks a merge is an org decision and not ours to make.
Start it narrow enough to be believable. Widen it when it’s earned that.
The fix that resolves it vs. the fix that passes the check
Here’s the thing I didn’t appreciate until I worked on this. Take the case from the top of the post. The original:
<div className="btn" onClick={handleSubmit}>
Submit
</div>
Looks fine. Works fine if you have a mouse. A keyboard never lands on it, because a div isn’t focusable. A screen reader reaches it and has nothing useful to say about it because it has no role and no accessible name.
Now, here’s a fix:
<div
className="btn"
role="button"
aria-label="Submit"
onClick={handleSubmit}
>
Submit
</div>
That will pass a name-role-value check. It is also still broken. It’s not in the tab order. It doesn’t respond to Enter or Space. A keyboard user is exactly as stuck as they were, and now the rule is green, which is worse than the rule being red.
The fix:
<button type="button" className="btn" onClick={handleSubmit}>
Submit
</button>
And if you genuinely can’t change the element. Some wrapper, some library, some reason, then it’s tabIndex={0}, plus role=”button”, plus a keydown handler for Enter and Space, plus remembering that you now own the focus styles the native element gave you for free.
Three plausible fixes. One resolves the problem, one silences the rule, one is a maintenance liability you’ve signed up for on purpose. For more guidance, see how to resolve an issue. Telling them apart is a judgment about what the thing is meant to do. That’s the part we’ve spent years building up: the agent runs around 130 rules based on accessiBe’s own scan, audit, and remediation data, and it’s framework-aware, so what it proposes fits the code it’s sitting next to rather than a snippet you have to port.
That’s also why a person has to approve every change. Some of these calls are about design intent, and design intent lives in the head of whoever wrote the component. Not in a model, and not in me reading their diff five weeks later.
Why the pull request specifically
At the pull request, three things are in one place at one time: the code, the person who wrote it, and the decision about what to do with it.
Downstream, they scatter. The code is in the repo. The person is three sprints into something else. The decision belongs to whoever the ticket was assigned to, who may never have opened that file. Every one of those six steps is machinery for reassembling those three things after the fact.
Review at the PR, and the machinery has nothing left to reassemble, because the split that made the work necessary never happened.
What it won’t do
- It can’t see the rendered experience. Source review catches things before they ship but has no idea what the page actually does at runtime. Live scanning sees the real thing but can’t point at a line. They’re complements. Code Agent is the source half.
- It doesn’t hand you a compliance verdict. Testing against a standard is an activity, not a result. It tells you what it found, measured against WCAG 2.2. Conformance is a claim about a whole product, and nobody responsible makes it off the back of a diff.
- It reviews new code today. The agent works on the pull request. Remediation across code that’s already sitting in your repo is coming soon.
Try it
Code Agent is live in accessFlow on Professional tier and above.
If you’re not sure whether it’s switched on for your workspace, your account manager will know.
And if a screenshot with a red rectangle on it turns up in your queue next week, at least now you know why it annoys you.