axe-core ElementInternals gap
apps/docs/src/content/docs/accessibility/axe-element-internals-gap Click to copy apps/docs/src/content/docs/accessibility/axe-element-internals-gap Summary
Section titled “Summary”axe-core 4.11.x — the engine the formal AAA audit harness runs alongside — does
not read ARIA role and accessible-name semantics that a custom element
exposes via the ElementInternals API. As a result,
axe will emit false-positive violations on HELiX form-associated custom
elements (FACE) for rules that rely on the AT-exposed role/name pair, even
when the component is in fact correct under WCAG 2.2 AAA (the carry-forward of every 2.1 SC that hasn’t changed).
This page is the canonical reference for that gap, the components it affects, the project mitigation, and the resolution path.
The technical gap
Section titled “The technical gap”ElementInternals (Form-Associated Custom Elements, FACE) lets a custom
element expose ARIA semantics — role, name, description, value, validity
state — to assistive technology without requiring the component author
to mirror those onto host attributes. The browser’s accessibility tree
reflects them; ATs (NVDA, JAWS, VoiceOver, TalkBack) read them correctly.
axe-core 4.x predates that platform feature in its rules pipeline. Its
checks for aria-allowed-attr, aria-required-children,
aria-required-parent, button-name, and the role-resolution helpers all
read the host element’s attributes (and the rendered DOM subtree) to
infer role / name. A FACE component whose role is _internals.role = 'combobox'
and whose name is _internals.ariaLabel = '…' looks “role-less” / “name-less”
to those checks, even though the rendered AT tree carries both.
The fix has been tracked in axe-core for years:
- axe-core PR #5080 — partial support landed behind a feature flag, still unmerged into a stable 4.x release as of 2026-05.
- axe-core issue #4259 — the root tracking issue, opened in 2023 and still open. The current upstream milestone is axe-core 4.12.
- Westbrook / axe-core-element-internals — community
patch-set demonstrating that the gap is well-known among web component
authors and that the workaround is to read
_internals.role/_internals.ariaLabeldirectly.
Affected HELiX components
Section titled “Affected HELiX components”Every HELiX component that opts into FACE
(static formAssociated = true plus attachInternals()) is subject to this
gap when audited with axe-core alone:
hx-buttonhx-checkboxhx-checkbox-grouphx-color-pickerhx-comboboxhx-date-pickerhx-file-uploadhx-icon-buttonhx-number-inputhx-radio-grouphx-ratinghx-selecthx-sliderhx-switchhx-text-inputhx-textareahx-time-pickerhx-toggle-button
Specifically the rules aria-allowed-attr, aria-required-children,
aria-required-parent, and button-name will produce false-positive
violations against these components when run via axe-core ≤ 4.11.x.
Our mitigation
Section titled “Our mitigation”- Formal audit uses measured evidence, not axe rule output.
pnpm aaa:audit(alias forscripts/aaa-formal-audit.mjs) sources every per-criterion verdict from static source checks (decorator + JSDoc + CSS + token-resolution sweeps), Playwright-driven DOM and geometry probes, focus-appearance and target-size measurements, axe contrast sweeps, and APG-aligned keyboard contracts. It does not delegate to axe for the rules listed above. The formal audit is the cert authority. See Self-certification scope for what the harness does and does not measure. - Story-audit harness suppresses the affected rules per-component.
scripts/audit-stories.mjsdisables the affected axe rules for the FACE component set so the harness does not emit known false positives. Real failures (color-contrast, target-size, label association in the light-DOM, etc.) still surface. - AT smoke runs are tracked separately. Per the self-certification scope, assistive- technology smoke runs (NVDA, JAWS, VoiceOver, TalkBack, Orca) are tracked separately from the cert badge — the harness simulates keyboard and inspects DOM but does not drive AT directly.
- Per-component host-attribute mirroring where it helps. Several FACE
components (
hx-select,hx-combobox, others) intentionally mirror role and ARIA state onto host attributes for legacy engines, axe-core compatibility, and CSS /getAttributeparity. Components that don’t need that fallback rely onElementInternalsalone. The choice is per-component, not a blanket policy.
Resolution path
Section titled “Resolution path”When upstream lands full ElementInternals support — currently tracked against the axe-core 4.12 milestone, with PR #5080 the in-flight implementation — we will:
- Bump
axe-corein the harness root and verify the previously-suppressed rules pass clean against every FACE component. - Remove the per-rule suppressions from
scripts/audit-stories.mjs. - Update this document with the resolution date and version, and demote the page to a historical reference.
Until then, treat any axe finding under one of the suppressed rules on a FACE component as the harness’s gap, not the component’s, and fall back to the formal audit transcript and AT evidence for the canonical verdict.
See also
Section titled “See also”- Formal AAA audit harness — the cert authority for HELiX.
- VPAT 2.5 — per-component conformance status.
- WAI-ARIA Authoring Practices — pattern reference. APG examples are written against host-attribute ARIA, not ElementInternals; the patterns themselves carry over but the testability story differs.