fix(aria-allowed-role): allow roles on a non-details summary - #5242
Conversation
A summary that is not its parent details element's disclosure trigger is exposed as generic and may take any role. A summary that is the details summary continues to allow no explicit role. Per ARIA in HTML (w3c/html-aria#435). The attribute-side of the spec change (disallowing aria-expanded and aria-pressed on a details summary) is tracked separately in #5241. Closes #3911
Drop the inside-shadow-DOM aria-allowed-role case (no boundary crossing), the generic get-element-spec summary cases (covered by integration), and the implicit-button-role assertion (summary's implicit role is a separate question being tracked independently).
943e379 to
9d0d4fb
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates axe-core’s HTML element standards data so <summary> role allowance matches the ARIA in HTML clarification: a <summary> that is the disclosure trigger for its parent <details> disallows author-specified roles, while other <summary> elements allow any role. This is implemented via a variant condition on the summary element spec and is exercised via unit, integration, and virtual-rule tests.
Changes:
- Update
lib/standards/html-elms.jsto resolve<summary>allowedRolesvia a variant that detects whether it is the first<summary>child of a<details>. - Add unit + integration + virtual-rule coverage for: details-summary (role disallowed), non-first summary (any role allowed), and standalone summary (any role allowed).
- Extend existing
aria-allowed-rolefixtures to include new<details>/<summary>cases.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/integration/virtual-rules/aria-allowed-role.js | Adds virtual-rule cases covering the new summary role-allowance behavior. |
| test/integration/rules/aria-allowed-role/aria-allowed-role.json | Adds pass/fail selectors for the new summary role cases. |
| test/integration/rules/aria-allowed-role/aria-allowed-role.html | Adds <details>/<summary> and standalone <summary> fixtures for integration coverage. |
| test/checks/aria/aria-allowed-role.js | Adds unit tests asserting allowed/unallowed roles for details-summary vs non-details summary. |
| lib/standards/html-elms.js | Introduces a summary element variant to conditionally set allowedRoles. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…the summary variant Replace the inline condition function in the summary variant with the serializable isSummaryForDetails matcher from #5262 so the standards object stays function-free. The matcher intentionally throws for a disconnected (parentless) node, so the virtual-rule test now gives the summary a non-details parent.
…variant Per Steve's figure-PR review conventions: remove the spec-defined variant comments and drop the duplicated check and virtual-rule tests (the aria-allowed-role integration tests cover the summary cases).
…allowances # Conflicts: # test/integration/rules/aria-allowed-role/aria-allowed-role.html # test/integration/rules/aria-allowed-role/aria-allowed-role.json
|
@straker rebased onto develop — the merge conflicts are resolved. They were additive fixture collisions with the figure fixtures from #5240 that landed on develop, so I kept both the figure and summary cases in the |
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 🤖 🎉
Updates the roles allowed on a
<summary>element based on whether it is its parent<details>element's disclosure trigger, per the ARIA in HTML update.What & why
Per ARIA in HTML (
#el-summary, w3c/html-aria#435):summarythat is the "summary for its parentdetails" (the firstsummarychild of adetails) allows no explicit role — browsers expose it as the disclosure trigger.summarythat is not the details summary (standalone, or a 2nd+summary) is exposed as generic and allows any role.axe previously modeled
summarywith an unconditionalallowedRoles: false. This converts it to avariantusing aconditionmatcher (the same pattern introduced forfigurein #3443).Scope — role side only
Issue #3911 covers both the allowed roles and the allowed
aria-*attributes forsummary. This PR implements the role side. The attribute side (the spec disallowsaria-expanded/aria-pressedon a details-summary) is intentionally deferred — it requires an element-level disallowed-attribute mechanism axe does not have today, and is tracked in #5241.Behavior change
A standalone / non-details
<summary>now permits any role, where previously only the implicitbuttonrole was allowed. This is a spec-correct loosening; a<details>-summary's role handling is unchanged in effect (still restricted to its implicitbuttonrole).Tests
get-element-specunit — variant resolution for the three cases (details summary, non-first summary, standalone).aria-allowed-rolecheck unit — incl. an open Shadow DOM case.aria-allowed-rolevirtual-rule — details/summary structures built withSerialVirtualNode, incl. the no-parent default.aria-allowed-roleintegration HTML/JSON — first-summary fail, 2nd-summary pass, standalone pass, implicit-button pass.Closes #3911