ReleaseCode / SDK · ActionableHabr AI→ original

Blind developer teaches an AI agent not to invent accessibility bugs in code

A blind programmer is developing Accessibility Auditor Skill, a set of instructions for an AI agent that checks interface accessibility. Early versions judged a single file in isolation: they flagged a “missing” attribute that the library already supplied and modified shared components without examining their consumers. The author refined the rules based on real pull requests to open-source projects, and one of them, for Nextcloud iOS, was accepted.

AI-processed from Habr AI; edited by Hamidun News
Blind developer teaches an AI agent not to invent accessibility bugs in code
Source: Habr AI. Collage: Hamidun News.
Listen to article
Article Fact Sheet & Key Figures
ParameterDetails
Primary sourceHabr AI ↗
Editor-in-Chief
Key metric #1Check the component implementation and library version before reporting a problem
Key metric #2When adding a property to a shared interface, find all of its consumers
Key metric #3In its report, the agent states what it verified: code, tests, or a screen reader

Imagine an AI agent reviewing a form and confidently reporting that a field lacks aria-describedby. You open the code and discover that the form component from the library has long handled the association with the hint. False positives like these erode trust in an audit faster than missed bugs. A blind programmer who uses a screen reader encountered this problem while building Accessibility Auditor Skill, a set of instructions for an agent that checks accessibility in source code and on live websites.

Look at the implementation, not the call site

The key change to the skill is that, before labeling something a problem, the agent traces it from the usage site to the implementation. It determines which element receives focus, where its accessible name comes from, where the label, hint, and error message are created, how identifiers are passed, and what the installed library version actually does. It also checks whether the file it found even participates in the relevant flow: repositories often contain old components with similar names, and changing such a file makes no difference to users.

If the association is genuinely missing in a shared component, that is where it should be fixed, followed by checks of every affected field. If the association already exists, there is no need to duplicate it.

Lessons from specific pull requests

In a pull request for RTL, the agent examined authentication and loading messages. The status and alert roles already determine announcement behavior, so an extra assertive setting or role="alert" may interrupt screen-reader speech. For a spinner beside text, the rule is this: if the message is already present as text, the decorative indicator should not receive the same accessible name and should instead be hidden with aria-hidden. That pull request was closed without being merged, so the excerpt shows only the proposed change; the actual announcement behavior must be tested in the running application.

In Vets-Who-Code, the feedbackId property was added too broadly to a shared interface, making it available to components that did not handle it. This led to another rule: when adding a property, identify which components support it and whether it will end up as an extraneous HTML attribute. Identifiers are checked in both variants: generated internally and supplied from outside. Otherwise, the default example works while a form using its own ID loses its association with the error.

In suitenumerique/docs, the fix depended on an internal element of Cunningham Select. The library may change its markup and break the selector. The skill now favors the public API and, when internal implementation details must be used, requires a regression test for the resulting aria-hidden and tabindex values. In that iteration, those checks were added to a Playwright test, but the full end-to-end suite was not run.

An accepted result and honest limitations

The only accepted pull request cited is Nextcloud iOS #4111, accepted on May 28, 2026. Folder rows in the save-location picker lacked an accessible name: VoiceOver focused the element but did not announce the folder name. The agent assembled the name from the title and the details already displayed in the cell, discarded empty values, and assigned the button trait to the row. Only the source code was reviewed: no iOS build or VoiceOver test was performed because Xcode was unavailable in the environment.

The author explicitly says there is no before-and-after measurement of the false-positive rate. What exists are the rules and the cases that prompted them. The agent must therefore state exactly what it did in its report: inspected the code, ran tests, or listened with a screen reader. These represent different levels of confidence.

Key takeaways

If you build agents for code review, do not limit them to a single file. Make the agent trace the chain from the call site to the library and inspect the consumers of shared components. Record lessons from real pull requests directly in the instructions so they do not have to be explained again in every project. And require the agent to name the type of verification honestly: reading code is not a substitute for testing with a screen reader on a device.

Story

Key Questions Answered

What are the key details on Look at the implementation, not the call site?

The key change to the skill is that, before labeling something a problem, the agent traces it from the usage site to the implementation. It determines which element receives focus, where its accessible name comes from, where the label, hint, and error message are created, how identifiers are passed, and what the installed library version actually does.

What are the key details on Lessons from specific pull requests?

In a pull request for RTL, the agent examined authentication and loading messages. The status and alert roles already determine announcement behavior, so an extra assertive setting or role="alert" may interrupt screen-reader speech. For a spinner beside text, the rule is this: if the message is already present as text, the decorative indicator should not receive the same accessible name and should instead be hidden with aria-hidden.

What are the key details on An accepted result and honest limitations?

The only accepted pull request cited is Nextcloud iOS #4111, accepted on May 28, 2026. Folder rows in the save-location picker lacked an accessible name: VoiceOver focused the element but did not announce the folder name. The agent assembled the name from the title and the details already displayed in the cell, discarded empty values, and assigned the button trait to the row. Only the source code was reviewed: no iOS build or VoiceOver test was performed because Xcode was unavailable in the environment.

ZK
Zhemal Khamidun — CPO at Alpina Digital, product manager of AlpinaGPT. Building AI products since 2019. Hamidun News is a pet project that grew into a production aggregator with 6,000+ readers.
✦ Hands-On AI Engineering · Hamidun Academy

Build & Ship Production AI Agents Without Boilerplate Code

Start your free 72-hour trial at Hamidun Academy (academy.hamidun.com) — interactive lessons on Claude Code & Cursor, live Tech Radar, and production agent templates.

Start 72h Free Trial →Vibe-Coding Intensive
What do you think?
Loading comments…