Skip to content
HELiX

axe-core ElementInternals gap

apps/docs/src/content/docs/accessibility/axe-element-internals-gap Click to copy
Copied! apps/docs/src/content/docs/accessibility/axe-element-internals-gap

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.

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.ariaLabel directly.

Every HELiX component that opts into FACE (static formAssociated = true plus attachInternals()) is subject to this gap when audited with axe-core alone:

  • hx-button
  • hx-checkbox
  • hx-checkbox-group
  • hx-color-picker
  • hx-combobox
  • hx-date-picker
  • hx-file-upload
  • hx-icon-button
  • hx-number-input
  • hx-radio-group
  • hx-rating
  • hx-select
  • hx-slider
  • hx-switch
  • hx-text-input
  • hx-textarea
  • hx-time-picker
  • hx-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.

  1. Formal audit uses measured evidence, not axe rule output. pnpm aaa:audit (alias for scripts/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.
  2. Story-audit harness suppresses the affected rules per-component. scripts/audit-stories.mjs disables 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.
  3. 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.
  4. 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 / getAttribute parity. Components that don’t need that fallback rely on ElementInternals alone. The choice is per-component, not a blanket policy.

When upstream lands full ElementInternals support — currently tracked against the axe-core 4.12 milestone, with PR #5080 the in-flight implementation — we will:

  1. Bump axe-core in the harness root and verify the previously-suppressed rules pass clean against every FACE component.
  2. Remove the per-rule suppressions from scripts/audit-stories.mjs.
  3. 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.

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