feat(a11y): every widget describes itself, and an image can finally be named - #172
Merged
Conversation
…e named Two gaps, found by auditing the accessibility layer rather than by hitting them. Image reported RoleImg with NO name and had no field to hold one. Its own doc comment said "Name is left for the host to fill in from context" — which A11y()'s by-value return makes impossible. An image that cannot be described is the one thing accessibility exists to prevent, so Image gains Alt, and Thumbnail gains it too (falling back to the caption it already shows). Twenty-one of 113 widgets had no A11y() at all. That is worse than an imperfect role: CollectA11y silently SKIPS a widget that does not implement Accessible, so a consumer could not tell "this has no semantics" from "nobody wrote its A11y()", and the gap stayed invisible. All 113 now answer. Layout and decoration answer RolePresentation. That is not filler — it is ARIA's own "look through me to the content inside", and it is now a stated position rather than an omission. CollectA11y filters those out, so its callers see exactly what they saw before: the meaningful nodes, no structural furniture. The two existing tests that assert an HBox is skipped pass unchanged, which is the evidence that this preserves the contract rather than reinterpreting it. The rest carry real semantics: Browser is a document named by the page it shows, PagingToolbar's value is "page 2 of 7", a LoadMask says "busy" only while it actually is, a StatusIcon announces its badge count, Window is a dialog named by its title. TestEveryWidgetIsAccessible parses the package and fails naming any widget that describes itself as nothing, so the gap cannot reopen quietly. 100% coverage held; race-clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two gaps, found by auditing the accessibility layer rather than by hitting them.
An image could not be described
ImagereportedRoleImgwith no name, and had no field to hold one. Its own doc comment said "Name is left for the host to fill in from context" — whichA11y()'s by-value return makes impossible.An image that cannot be described is the one thing accessibility exists to prevent.
ImagegainsAlt;Thumbnailgains it too, falling back to the caption it already shows.Twenty-one of 113 widgets said nothing at all
Worse than an imperfect role:
CollectA11ysilently skips a widget that does not implementAccessible, so a consumer could not tell "this has no semantics" from "nobody wrote itsA11y()". The gap stayed invisible. All 113 now answer.Layout and decoration answer
RolePresentation. That is not filler — it is ARIA's own "look through me to the content inside", and it is now a stated position rather than an omission.CollectA11yfilters those out, so its callers see exactly what they saw before: the meaningful nodes, no structural furniture. The two existing tests asserting anHBoxis skipped pass unchanged — which is the evidence this preserves the contract rather than reinterpreting it.The rest carry real semantics:
BrowserPagingToolbarpage 2 of 7LoadMaskbusy, only while it actually isStatusIconWindow/WindowDecorationGanttThe guard
TestEveryWidgetIsAccessibleparses the package, finds every struct embeddingBaseand everyA11y()method, and fails naming any widget that describes itself as nothing. The gap cannot reopen quietly.Verification
go test -race→ 100.0% of statements, race-clean.🤖 Generated with Claude Code