Skip to content

feat(aria-prohibited-attr): allow many elements to be named and disallow label and body from being named - #5259

Merged
WilcoFiers merged 14 commits into
developfrom
aria-prohibited-fix
Aug 5, 2026
Merged

feat(aria-prohibited-attr): allow many elements to be named and disallow label and body from being named#5259
WilcoFiers merged 14 commits into
developfrom
aria-prohibited-fix

Conversation

@straker

@straker straker commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

After looking over the HTML in ARIA spec, I realized that we could better handle how we determine when an attribute is prohibited. Basically if the element doesn't have an explicit role, then we use the htmlElm spec to determine prohibited attrs and naming, otherwise we use the ariaRoles spec to determine that. This should allow us to eliminate the elementsAllowedAriaLabel option that was introduced to fix which elements allowed naming and then modified when we added Chromium roles to fix conflict name resolution. However the option still has the applet element which isn't in our html spec so I'm leaving it for now.

Elements which allow naming now include: section, blockquote, address, hgroup, ruby.

This also adds enforcement for elements that are not allowed any aria-* attribute (which has never been used in our code) by changing the noAriaAttrs spec property to an empty allowedAriaAttrs so that they now throw in the rule aria-allowed-attr.

Closes: #5185
Closes: #3410

@straker
straker requested a review from a team as a code owner July 27, 2026 15:52

@WilcoFiers WilcoFiers left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup — replacing the "no role at all means no name" inference with explicit spec data is clearly the right direction, and it fixes a batch of long-standing false positives. I built both the merge-base and this branch and diffed rule output across ~60 markup cases to see exactly what moves. Two things need a decision before merge.

Note: the failing test_node (18) and test_node (20) checks are an unrelated undici/jsdom incompatibility, not caused by this PR.


1. del loses naming prohibition while ins keeps it (blocking)

lib/standards/html-elms.js L215 (compare ins at L566)

del didn't get namingProhibited: true, but ins did:

<del aria-label="hello"></del>
  before: violation — aria-label attribute cannot be used on a del with no valid role attribute.
  after:  PASS

<ins aria-label="hello"></ins>
  before: violation
  after:  violation

Naming is prohibited on both — del maps to deletion and ins to insertion, and both carry prohibitedAttrs in aria-roles.js. Since neither element appears in implicitHtmlRoles, the role spec is no longer consulted, so del silently drops its prohibition.

Suggestion: add namingProhibited: true to del, and add a test case pairing del with ins so the two can't drift apart again.


2. body and dd report a role whose spec allows naming (blocking)

lib/checks/aria/aria-prohibited-attr-evaluate.js L76, with body at html-elms.js L135 and dd at L211

These are the only two elements given namingProhibited: true that also have a non-null implicit role, and two problems surface there.

The outcome is right but the reported role is wrong. <body aria-label> should fail — but axe maps body to document and dd to definition, and neither role has prohibitedAttrs, so the message blames a role that permits naming:

<body aria-label="hello">    before: PASS
                             after:  incomplete — aria-label attribute is not well supported with role "document".
<dd aria-label="hello">      before: PASS
                             after:  violation — aria-label attribute cannot be used with role "definition".

I confirmed aria-allowed-attr still passes both, so the two rules now disagree with each other. The real fix is in the mapping: body should be generic and dd should have no corresponding role. Worth noting generic doesn't exist in lib/standards/aria-roles.js at all today, so that correction is a prerequisite rather than a one-liner.

role === implicitRole isn't a safe proxy for "no explicit role." An author can write <dd role="definition"> or <p role="paragraph"> and land in the HTML-spec branch even though an explicit role was set — which is the case the ARIA role spec should govern.

Suggestion: branch on getExplicitRole() rather than comparing the resolved role to the implicit role, and fix the body/dd mappings (adding generic to aria-roles.js) so the prohibition and its message come from the same place. If the mapping fix is too large for this PR, splitting it out would at least keep body and dd from shipping with contradictory messaging in the meantime.


3. Four more elements correctly stop prohibiting names (note)

Beyond del, four elements flip from violation to pass, all correctly:

<address aria-label="hello">     before: violation   after: PASS
<blockquote aria-label="hello">  before: violation   after: PASS
<hgroup aria-label="hello">      before: violation   after: PASS
<ruby aria-label="hello">        before: violation   after: PASS

These were false positives from the old catch-all — implicitHtmlRoles only lists elements whose role axe actually needs, so every known HTML element in that gap fell through to return ['aria-label', 'aria-labelledby']. This is a fix, not a regression.

Suggestion: call these out in the PR description so they land in the release notes, and add test cases pinning the new behavior so we don't regress them in the future.


4. doc/check-options.md documents a stale default (non-blocking)

doc/check-options.md L98

The PR changes the elementsAllowedAriaLabel default to ["applet"], but the docs still advertise a twelve-element list including label — precisely what this PR reverses. The table was already stale, but this makes it actively contradict the fix in the PR title. Two-line change while you're in there.


5. noAriaAttrs branch reuses the noRole messages (non-blocking, separate issue)

lib/checks/aria/aria-prohibited-attr-evaluate.js L88

<col aria-invalid="true">
  violation — aria-invalid attribute cannot be used on a col with no valid role attribute.

The wording implies a valid role would fix it, but col permits no ARIA attributes at all regardless of role. Same message now reaches map, picture, base, head, html, link, meta, noscript, param, script, slot, source, style, template, title, and track. A dedicated message key would read better — worth its own issue rather than expanding this PR.

@straker straker changed the title fix(aria-prohibited-attr): allow section to be named and disallow label from being named fix(aria-prohibited-attr): allow many elements to be named and disallow label from being named Jul 27, 2026
@straker straker changed the title fix(aria-prohibited-attr): allow many elements to be named and disallow label from being named fix(aria-prohibited-attr): allow many elements to be named and disallow label and body from being named Jul 27, 2026
chutchins25
chutchins25 previously approved these changes Jul 29, 2026
Comment thread lib/checks/aria/aria-prohibited-attr-evaluate.js Outdated
Comment thread lib/checks/aria/aria-prohibited-attr-evaluate.js Outdated
Comment thread lib/checks/aria/aria-prohibited-attr-evaluate.js Outdated
Comment thread test/checks/aria/aria-prohibited-attr.js
Comment thread lib/standards/html-elms.js
Comment thread lib/standards/html-elms.js
Comment thread lib/standards/html-elms.js
Comment thread lib/standards/html-elms.js
contentTypes: ['phrasing', 'flow'],
allowedRoles: false,
noAriaAttrs: true
allowedAriaAttrs: ['aria-hidden']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a test too.

@straker straker Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out we can't test this as aria-prohibited-attr ignores elements that aren't visible to screen readers

<iframe
aria-label="value"
id="pass82"
srcdoc="<main><h1>hello</h1></main>"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was tired of having this fail whenever I changed the playground code. Now it should always pass

@straker straker changed the title fix(aria-prohibited-attr): allow many elements to be named and disallow label and body from being named feat(aria-prohibited-attr): allow many elements to be named and disallow label and body from being named Aug 4, 2026
@WilcoFiers
WilcoFiers merged commit d8b1ea5 into develop Aug 5, 2026
23 checks passed
@WilcoFiers
WilcoFiers deleted the aria-prohibited-fix branch August 5, 2026 09:46
WilcoFiers added a commit that referenced this pull request Aug 5, 2026
All notable changes to this project will be documented in this file. See
[commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version)
for commit guidelines.

##
[4.13.0](v4.12.1...v4.13.0)
(2026-08-05)

### Features

- **aria-actions:** add aria-actions to allowed ARIA attributes
([#5200](#5200))
([029655d](029655d)),
closes [#4584](#4584)
[#5199](#5199), references
[#5215](#5215)
[#5215](#5215)
- **aria-allowed-attr:** flag deprecated ARIA attributes as needs-review
([#5246](#5246))
([518f3cc](518f3cc)),
closes [#3341](#3341)
- **aria-prohibited-attr:** allow many elements to be named and disallow
label and body from being named
([#5259](#5259))
([d8b1ea5](d8b1ea5))
- **aria-roles:** add sectionheader and sectionfooter roles
([#5238](#5238))
([c36c109](c36c109)),
closes [#4734](#4734),
references [#4734](#4734)
- **aria/get-aria-value:** new function to get aria values of a node
([#5109](#5109))
([a7d8f3e](a7d8f3e)),
references [#5042](#5042)
- **aria/has-attr-value:** new function to check if node has aria value
([#5136](#5136))
([61f2624](61f2624)),
references [#5109](#5109)
- **aria:** support role=image as equivalent to role=img
([#5248](#5248))
([5aa8aaf](5aa8aaf)),
closes [#4656](#4656),
references [#5272](#5272)
- **checks/aria:** support ARIA element internals properties
([#5172](#5172))
([9b7f754](9b7f754))
- **checks/label:** support ARIA element internals properties
([#5170](#5170))
([21c5f8b](21c5f8b))
- **checks/navigation:** support ARIA element internals properties
([#5167](#5167))
([2c3a98f](2c3a98f))
- **commons/aria:** support ARIA element internals properties
([#5171](#5171))
([31f09e7](31f09e7))
- **commons/dom:** support ARIA element internals properties
([#5163](#5163))
([f0a12cf](f0a12cf))
- **commons/forms:** support ARIA element internals properties
([#5165](#5165))
([27a4686](27a4686))
- **commons/matches/fromPrimative:** deprecate in favor of correct
spelling ([#5270](#5270))
([31cfb2e](31cfb2e))
- **commons/text:** support ARIA element internals properties
([#5169](#5169))
([e841a33](e841a33))
- **commons/text:** support form-associated labels via element internals
([#5182](#5182))
([57cfe0a](57cfe0a)),
closes [#5045](#5045),
references [#5170](#5170)
[#5039](#5039)
[#5151](#5151)
[#5039](#5039)
- **dom/getResolvedRefs:** new function to get the resolved virtual
nodes of idrefs
([#5151](#5151))
([489cdea](489cdea)),
references [#5109](#5109)
- **element-internals:** enable ElementInternals by default
([#5284](#5284))
([2740d42](2740d42)),
closes [#5277](#5277)
- **i18n:** Add Swedish locale
([#5190](#5190))
([dcd13f2](dcd13f2)),
references [#5189](#5189)
- **matches:** add inSectioningContent, hasChild, and
isSummaryForDetails matches
([#5262](#5262))
([c47cdcd](c47cdcd))
- **rules:** support ARIA element internals properties
([#5168](#5168))
([065baf7](065baf7))
- **standards/ariaAttrs:** add caseInsensitive property for attributes
([#5224](#5224))
([bcd791c](bcd791c))

### Bug Fixes

- **aria-allowed-role:** allow roles on a non-details summary
([#5242](#5242))
([3bd9875](3bd9875)),
closes [#3911](#3911),
references [#3443](#3443)
[#3911](#3911)
- **aria-allowed-role:** restrict figure roles with child figcaption
([#5240](#5240))
([178a635](178a635)),
closes [#3443](#3443)
- **aria-prohibited-attr:** visible aria-labelledby requires review only
([#5285](#5285))
([fd6fa9f](fd6fa9f))
- **axe.d.ts:** make enabled property of RuleMetadata optional
([#5129](#5129))
([90fce18](90fce18))
- **color-contrast:** fix various stacking context bugs
([#5214](#5214))
([d5e5b04](d5e5b04)),
references [#8](#8)
[#5213](#5213)
- **gather-internals:** handle non-HTMLElement nodes
([#5161](#5161))
([06e84c3](06e84c3))
- **get-selector:** escape control characters in attribute selectors
([#5273](#5273))
([4b60ac5](4b60ac5)),
closes [#5204](#5204)
[#5204](#5204)
- **image-alt:** allow whitespace alt on presentational images
([#5218](#5218))
([c5dd0ef](c5dd0ef)),
closes [#5216](#5216)
- **landmark-unique:** exclude section/form with non-landmark roles from
landmark match
([#5085](#5085))
([c5fd013](c5fd013)),
closes [#4722](#4722)
[#5064](#5064)
- name the image role in role-img-alt and svg-img-alt metadata
([#5279](#5279))
([995a269](995a269)),
closes [#5272](#5272),
references [#5248](#5248)
[#5248](#5248)
- **standards:** update aria-errormessage and aria-details to be idrefs
([#5157](#5157))
([fb94f8a](fb94f8a))

This PR was opened by a robot 🤖 🎉
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

section / aside elements should allow empty aria-labelledby Two additional elements that prohibit naming from authors

3 participants