Accessibility checklist
This checklist helps reviewers perform an accessibility review of a patch. It is designed to be used alongside the Accessibility coding guidelines and the Accessibility testing documentation.
- Backend only (web services, database, events, hooks)? → No accessibility review needed.
- Changes to HTML, CSS, or JavaScript? → Accessibility review is necessary. Use this checklist.
- Only standard HTML elements, no custom behaviour? → A quick check is recommended, but a full review may not be needed.
Quick-reference checklist
Use this as a quick scan. If any item fails or you are unsure, refer to the detailed section linked in each item.
Automated
- Run Axe DevTools or WAVE on the affected page(s). Fix any issues before continuing.
Structure and semantics
- The page has a descriptive, unique page title with the most specific information first. (Details)
- There is exactly one
h1heading and the heading hierarchy has no skipped levels. (Details) - Semantic HTML elements are used (for example,
<button>for buttons,<nav>for navigation). (Details)
Keyboard and focus
- All interactive elements are reachable and operable using only the keyboard. (Details)
- No element uses
tabindexgreater than0. (Details) - Every focused element has a clearly visible focus indicator. (Details)
- No component traps keyboard focus. (Details)
Visual presentation
- Text and interactive elements meet colour contrast requirements. (Details)
- The page is usable at 200% and 400% zoom without overlap or horizontal scrolling. (Details)
- Colour is not the only means of conveying information. (Details)
- CSS visual order matches DOM order, or custom keyboard handling compensates. (Details)
- CSS animations respect
prefers-reduced-motion. (Details)
Content and alternatives
- Icons inside buttons/links are hidden from assistive technologies; the accessible name is on the interactive element. (Details)
- Decorative images have
alt="". Informative images have meaningful alt text. (Details) - Link text is descriptive — no "click here" or "read more". (Details)
- Buttons without visible text have an accessible name (
aria-labelor visually hidden text). (Details)
Dynamic content and custom widgets
- Dynamic updates use
aria-liveregions so screen readers are notified. (Details) - Custom widgets follow an ARIA APG pattern with correct roles, states, and keyboard interactions. (Details)
Forms
- Every form field has a
<label>(with matchingfor/id). (Details) - Invalid fields use
aria-invalidand error messages are linked viaaria-describedby. (Details)
Detailed guidance
The sections below provide more context for each checklist item. For full explanations and code examples, refer to the Accessibility coding guidelines.
Run an automated check
Run an automated accessibility checker on the affected page(s) before starting the manual review:
- Axe DevTools or WAVE Web Accessibility Evaluation Tool
- An HTML validator such as the Nu HTML validator
Any reported issues must be fixed before continuing with the manual review.
Related documentation
- Accessibility testing > Automated testing — browser extensions, built-in dev tools, and Behat accessibility tests
Page title and headings
- The page must have a descriptive, unique page title. The most specific information should come first (for example, "Submit assignment | Kinetics problem set 1 | Physics 101" rather than "Physics 101 | Activity").
- There must be exactly one
h1heading on the page. It should reflect the page title. - Headings must follow a strict hierarchy — no skipped levels (for example,
h2followed directly byh4).
Related documentation
Keyboard operability
- Tab through every interactive element on the page. Ensure all buttons, links, form fields, and custom widgets are reachable.
- Activate elements using
Enterand/orSpaceas appropriate. - Confirm that focus is never trapped — you must always be able to leave a component (for example, pressing
Escapeto close a modal). - Complete typical user tasks using only the keyboard.
Related documentation
tabindex values
tabindexgreater than0(for example,tabindex="1") is always wrong. Only0and-1are acceptable.- If
tabindex="0"is on a natively focusable element (<button>,<a>,<input>, etc.), it should be removed. - If
tabindex="0"is on a non-interactive element, the element must also have an appropriaterole(for example,role="button").
Related documentation
Visible focus indicators
- Every interactive element must show a clearly visible focus indicator when focused via keyboard.
- Moodle overrides default browser focus styles. For custom components, use CSS utility classes such as
.aalinkand.aabtn, or add custom focus styles.
Related documentation
Colour and contrast
- Text and interactive elements must meet the required contrast ratios (4.5:1 for normal text, 3:1 for large text).
- Colour must not be the only means of conveying information. If colour indicates a state (for example, red for errors), an additional cue such as text or an icon must also be present.
Related documentation
Zoom and reflow
- Set the browser viewport to 1280px wide and zoom to 200%. Verify the page remains usable.
- Set the viewport to 320px wide (or 1280px at 400% zoom). Verify content reflows into a single column without horizontal scrolling or overlapping elements.
Related documentation
CSS order versus DOM order
If CSS visually reorders elements (for example, using Flexbox order), keyboard navigation will still follow the DOM order. This discrepancy confuses keyboard users. Ensure the visual order matches the DOM order, or provide custom keyboard navigation.
Related documentation
Animations
CSS animations should respect the user's prefers-reduced-motion setting. From Moodle 4.3 onwards, use the optional-animation SCSS mixin to mark animations as non-essential.
Related documentation
Icons and images
- Icons inside buttons or links (decorative): Hide the icon from assistive technologies (empty
alt, oraria-hidden="true"). The accessible name should be on the button/link itself (aria-labelor a<span class="visually-hidden">). - Informative icons (conveying meaning not provided by surrounding text): Give the icon an accessible name (for example, via the
altattribute). - Decorative images: Use
alt="".
Related documentation
Buttons
- Buttons without visible descriptive text must have an accessible name.
- For icon-only buttons, hide the icon from assistive technologies and provide the accessible name on the button using
aria-labelor a<span class="visually-hidden">.
<button type="button" class="btn btn-primary">
<i class="icon fa-user-plus fa-fw" aria-hidden="true"></i>
<span class="visually-hidden">Add a new user</span>
</button>
Related documentation
Link text
- Link text must clearly describe the link's purpose. Avoid "click here", "read more", "here", or "more info".
- If multiple links share the same visible text but go to different destinations, add visually hidden text to make each link unique.
Related documentation
Custom components
When the patch introduces custom interactive widgets (for example, tabs, modals, dropdown menus), check that:
- The widget follows a recognised ARIA APG pattern.
- All keyboard interactions from the APG pattern are implemented.
- The correct ARIA roles, states, and properties are used and updated via JavaScript.
Related documentation
Dynamic content
If parts of the page are updated dynamically via JavaScript, check that:
- The container uses
aria-live("polite"for non-urgent updates,"assertive"for urgent ones) so screen readers announce changes. - While loading, the container has
aria-busy="true"(removed once complete). - Focus is managed — if content is inserted, focus moves to it; if content is removed, focus returns to the triggering element.
Related documentation
Forms
Unless the patch uses custom form fields, Moodle forms (moodleforms) are already accessible. For custom forms, check:
- Every field has a
<label>with aforattribute matching the field'sid. - The form is fully operable by keyboard.
- Invalid fields are marked with
aria-invalid="true"and error messages are linked viaaria-describedby. - Error messages are visible, clear, and descriptive.
When a patch introduces ad-hoc forms, ensure they follow the forms guidelines.
Related documentation
Other visual checks
- Visual structure: If empty space, indentation, or colour is used to convey hierarchy, the same structure must be represented in the HTML (for example, nested lists, headings, or
role="list"). - Hover/focus-only elements: If elements only appear on hover, ensure keyboard users can also reveal them. Moodle provides the
focus-control(on the parent) andv-parent-focus(on the child) CSS classes for this.
Provide feedback
If accessibility issues are found, explain:
- What the issue is.
- Why it is a problem (who is affected).
- How to fix it (with a reference to the relevant WCAG success criterion or coding guideline section where possible).
Further reading
- Accessibility coding guidelines — detailed guidance with code examples
- Accessibility testing — automated and manual testing techniques
- Deque University Web Accessibility Checklist — a more comprehensive general-purpose checklist