Skip to content

fix(aria-allowed-role): restrict figure roles with child figcaption - #5240

Merged
straker merged 6 commits into
developfrom
chut/3443-figure-figcaption
Aug 4, 2026
Merged

fix(aria-allowed-role): restrict figure roles with child figcaption#5240
straker merged 6 commits into
developfrom
chut/3443-figure-figcaption

Conversation

@chutchins25

Copy link
Copy Markdown
Contributor

Restricts the roles allowed on a <figure> element when it has a child <figcaption>, per the ARIA in HTML update.

What & why

Per ARIA in HTML (#el-figure, w3c/html-aria#415):

  • A figure with a valid figcaption descendant allows no role other than figure, plus the DPub role doc-example.
  • A figure without a figcaption allows any role (though figure itself is NOT RECOMMENDED) — unchanged.

axe previously modeled figure with a flat allowedRoles: true and an inline comment admitting it couldn't match the figcaption case. This replaces that with a variant using a condition matcher.

Implementation notes

  • The implicit figure role still passes via the existing allowImplicit short-circuit, so the with-figcaption branch only needs allowedRoles: ['doc-example'].
  • The matcher checks direct children (vNode.children?.some(...)), not a descendant walk: HTML's content model only permits figcaption as the first/last child of figure, and a direct-child check avoids a nested-<figure> false match (an inner figure's figcaption wrongly restricting the outer figure).

Behavior change

A <figure> with a <figcaption> and an explicit role other than figure/doc-example (including role="none"/role="presentation") now flags on aria-allowed-role (minor impact). This is spec-correct but will surface new findings for existing users.

Tests

  • get-element-spec unit — variant resolution with/without figcaption.
  • is-aria-role-allowed-on-element and aria-allowed-role check units — incl. an open Shadow DOM case.
  • aria-allowed-role virtual-rule — figcaption-child cases (built with SerialVirtualNode) plus the no-figcaption default.
  • aria-allowed-role integration HTML/JSON — pass (doc-example, implicit figure) and violation (group) cases; existing no-figcaption pass retained as a regression anchor.

Closes #3443

A figure with a child figcaption is now limited to its implicit figure
role and the DPub doc-example role, per ARIA in HTML (w3c/html-aria#415).
A figure without a figcaption continues to allow any role.

Closes #3443
Comment thread lib/standards/html-elms.js Outdated
Comment thread lib/standards/html-elms.js
@chutchins25
chutchins25 force-pushed the chut/3443-figure-figcaption branch from d39b1f3 to a63c18b Compare July 29, 2026 15:10
@chutchins25
chutchins25 marked this pull request as ready for review July 29, 2026 17:43
@chutchins25
chutchins25 requested a review from a team as a code owner July 29, 2026 17:43
Copilot AI review requested due to automatic review settings July 29, 2026 17:43

Copilot AI 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.

Pull request overview

This PR updates axe-core’s ARIA-in-HTML modeling for <figure> to reflect the spec change that a figure with a child <figcaption> is restricted to its implicit role (figure, handled via allowImplicit) and the explicit DPub role doc-example, while a figure without <figcaption> continues to allow any explicit role.

Changes:

  • Updated figure’s allowedRoles in the HTML standards map to use a variant that detects a direct-child figcaption and restricts roles accordingly.
  • Added/updated unit tests for element spec resolution, role-allowed logic, and the aria-allowed-role check (including Shadow DOM coverage).
  • Added integration + virtual-rule test coverage for pass/fail cases involving figure + figcaption.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
lib/standards/html-elms.js Introduces a figure variant that restricts allowedRoles to ['doc-example'] when a direct-child figcaption is present; otherwise defaults to allowedRoles: true.
test/commons/standards/get-element-spec.js Verifies variant resolution for figure with/without a figcaption child.
test/commons/aria/is-aria-role-allowed-on-element.js Adds direct unit coverage that doc-example is allowed and group is disallowed for figure with figcaption.
test/checks/aria/aria-allowed-role.js Adds check-level tests (including Shadow DOM) for the new figure+figcaption role restriction behavior.
test/integration/virtual-rules/aria-allowed-role.js Adds virtual-rule coverage using SerialVirtualNode for figure+figcaption pass/fail and the no-figcaption regression case.
test/integration/rules/aria-allowed-role/aria-allowed-role.html Adds integration fixtures for figure+figcaption with doc-example/figure (pass) and group (fail).
test/integration/rules/aria-allowed-role/aria-allowed-role.json Registers the new integration fixture selectors in passes/violations lists.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@chutchins25
chutchins25 marked this pull request as draft July 29, 2026 20:18
hasChild threw when vNode.children was undefined (e.g. a SerialVirtualNode
passed to runVirtualRule without children). Treat a missing children list
as no match.
…variant

Replace the inline condition function in the figure figcaption variant with
the serializable hasChild: 'figcaption' matcher from #5262 so the standards
object stays function-free. Behavior is unchanged.
@chutchins25
chutchins25 marked this pull request as ready for review August 3, 2026 20:23
Comment thread lib/commons/matches/has-child.js Outdated
Comment thread lib/standards/html-elms.js
Comment thread lib/standards/html-elms.js Outdated
Comment thread lib/standards/html-elms.js Outdated
Comment thread test/checks/aria/aria-allowed-role.js Outdated
Comment thread test/checks/aria/aria-allowed-role.js Outdated
Comment thread test/commons/matches/has-child.js Outdated
Comment thread test/commons/standards/get-element-spec.js
Comment thread test/integration/virtual-rules/aria-allowed-role.js Outdated
Per review: drop the hasChild children guard (children are assumed
present), remove the spec-defined variant comments, drop the duplicated
check and virtual-rule tests (integration covers them), and add a
get-element-spec test that a nested figcaption does not restrict the
outer figure.
@chutchins25
chutchins25 requested a review from straker August 4, 2026 13:45
@straker
straker merged commit 178a635 into develop Aug 4, 2026
23 checks passed
@straker
straker deleted the chut/3443-figure-figcaption branch August 4, 2026 14:23
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.

Update role allowance for figure with child figcaption

3 participants