Skip to content

feat(rules): support ARIA element internals properties - #5168

Merged
chutchins25 merged 9 commits into
developfrom
internals-rules
Jul 20, 2026
Merged

feat(rules): support ARIA element internals properties#5168
chutchins25 merged 9 commits into
developfrom
internals-rules

Conversation

@straker

@straker straker commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Updates the rules directory to use the new getAriaValue function where it makes sense. Only the JS match files were reviewed; JSON rule definitions have no ARIA attr access.

Here's a report of the full directory (JS files only):

  • rules/autocomplete-matches - aria-readonly and aria-disabled → getAriaValue with { lowercase: true } (both are boolean type); removed the now-redundant .toLowerCase() calls on the comparison
  • rules/aria-allowed-attr-matches - iterates over attrNames, no direct .attr('aria-*') call
  • rules/aria-has-attr-matches - checks for presence of any aria attr via attrNames, not a value lookup
  • rules/bypass-matches - checks for links and headings, no ARIA attr access
  • rules/color-contrast-matches - checks visibility, no ARIA attr access
  • rules/identical-links-same-purpose-matches - computes accessible names, no ARIA attr access
  • rules/inserted-into-focus-order-matches - checks tabindex, not an ARIA prop
  • rules/label-matches - checks element type and role, no direct aria attr value access
  • rules/landmark-has-body-context-matches - checks role, not an ARIA prop
  • rules/no-autoplay-audio-matches - checks media attributes, no ARIA attr access
  • rules/no-empty-role-matches - checks role attr, not an ARIA prop
  • rules/no-negative-tabindex-matches - checks tabindex, not an ARIA prop
  • rules/no-role-matches - checks role attr, not an ARIA prop

Closes: #5150

Updates autocomplete-matches.js to use getAriaValue for aria-readonly
and aria-disabled checks so that values set via ElementInternals are
also respected.
@straker
straker requested a review from a team as a code owner June 15, 2026 21:58
straker added 4 commits June 16, 2026 09:08
- aria-hidden-focus-matches: shouldMatchElement used el.getAttribute('aria-hidden')
  directly on a DOM node; convert to getAriaValue via getNodeFromTree with
  fallback to getAttribute when the node is not in the axe virtual tree
- color-contrast-matches: control.getAttribute('aria-labelledby') used to
  check which controls reference an ancestor; convert to vNode.attr() for the
  HTML attribute case, and getResolvedRefs for the property/internals case
  (where the attribute string is absent but element refs are set)
…nternals

Adds a full integration test verifying that a focusable element inside
a [aria-hidden=true] container whose ancestor has aria-hidden=true set
via elementInternals is excluded from the rule — no violations.
…on/rules

Add a testutils-element with aria-hidden via elementInternals containing
a focusable [aria-hidden=true] div. With the fix the inner element is
excluded by the matches function (ancestor internals aria-hidden detected)
so no new violation appears. Without the fix it would be a violation,
causing 'should not return other results' to fail.

@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.

LGTM, just needs to be updated to 5177

Comment thread lib/rules/aria-hidden-focus-matches.js Outdated
@chutchins25

chutchins25 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Heads up: the audit report in the PR description looks out of sync with the actual diff.

The description lists autocomplete-matches as the converted file and marks both color-contrast-matches and (implicitly) aria-hidden-focus-matches as having no ARIA attr access. But the diff actually converts:

  • lib/rules/aria-hidden-focus-matches.jsaria-hiddengetAriaValue
  • lib/rules/color-contrast-matches.jsaria-labelledbygetResolvedRefs / getAriaValue

…and doesn't touch autocomplete-matches. Worth refreshing the report so it matches what shipped before merge.

(Also updated the issue trailer — this is the rules/*-matches conversion, so it was pointing at the wrong issue. For history: changed from Refs: https://github.com/dequelabs/axe-core/issues/5142 to Closes: https://github.com/dequelabs/axe-core/issues/5150.)

@chutchins25

Copy link
Copy Markdown
Contributor

Following up on my earlier note about the autocomplete-matches audit/diff mismatch — I looked into whether it actually needs converting, and I think it's the one matches-file that doesn't.

autocomplete-matches early-returns on ['textarea', 'input', 'select'].includes(nodeName) === false, so it only runs on native form controls. Custom elements — the only carriers of ElementInternals — are excluded by that check, and aria-readonly/aria-disabled are reflected (ariaReadonly/ariaDisabled), so on a native element the property already syncs to the attribute attr() reads. There's no internals/property value getAriaValue would catch here that attr() misses.

The only behavioral difference would be getAriaValue's trimming — e.g. aria-readonly=" true " would flip from "not readonly" to "readonly". Arguably more correct, but unrelated to element internals.

This mirrors the aria-conditional-*-attr checks we just closed #5144 on (native-gated, no internals reachability). Since this one's a consumer rather than a validator, converting for uniformity is defensible — but it buys no internals coverage and adds a small behavior change. Your call: convert for consistency, or leave it literal and drop the autocomplete-matches line from the audit. The two files you did convert (aria-hidden-focus, color-contrast) do reach custom elements, so those are clearly worth it.

@chutchins25
chutchins25 merged commit 065baf7 into develop Jul 20, 2026
23 checks passed
@chutchins25
chutchins25 deleted the internals-rules branch July 20, 2026 17:59
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.

ElementInternals: convert lib/rules/*-matches to getAriaValue/hasAriaValue

3 participants