Skip to content

Accessibility

How the markup is audited

The audit no longer rests on a code review, but on an automated check replayed every time the tests run.

  1. tests/php/Unit/Accessibility/MarkupFixtureTest.php asks every field type to render itself, in its normal state and then in error, and writes the result to tests/js/fixtures/a11y/.
  2. tests/js/accessibility.test.js loads those documents into jsdom and submits them to axe-core.

The point that matters is that the audited markup is produced by the rendering code, never transcribed by hand into the test. A transcription would stay compliant while the real markup drifted; a real output fails the audit the moment a field loses its label or its ARIA association.

The escaping stand-ins of the unit bootstrap (esc_attr, esc_html, checked, selected) reproduce WordPress’s exactly — two calls to htmlspecialchars and two comparisons. The markup under examination is therefore the markup that would actually be served.

To replay the audit:

composer test:unit          # regenerates the documents
npm run test:unit:js        # audits them

What the audit caught

Putting it in place revealed two real defects, both invisible on reading:

  • aria-required on a fieldset (critical violation, five field types). A fieldset’s implicit role is group, which does not accept that attribute: it was therefore invalid and plainly ignored by screen readers, at the very moment we believed we were announcing that the field was required. Radio button and rating groups now declare role="radiogroup", which does accept it; checkbox groups and composite fields no longer write it at all, the requirement being carried by the visible asterisk, by each sub-field’s required attribute, and by server-side validation.
  • A consent checkbox with no label (critical violation). The consent text being configurable, clearing it produced an empty label, and so a mute checkbox. Rendering now falls back to the default wording.

Limits we accept

  • Colour contrast. axe-core’s color-contrast rule is disabled in this audit, for two reasons that compound: jsdom does not implement the canvas axe uses to measure colours, and the documents under examination load no stylesheet — so there would be nothing to measure. The contrast of the public stylesheets stays verified by hand.
  • The region rule. Disabled as well: the audited documents are isolated form fragments, with no header and no navigation. The rule would be judging the test’s scaffolding, not the field.
  • Keyboard behaviour. axe-core analyses a static tree. Moving between steps, adding a row to a repeater and shifting focus after an error are matters for interaction tests, not for this audit.
  • Page markup. The complete render — progress bar, multi-step pagination — is not covered here: it requires a real WordPress, and therefore the integration suite.