
Canvas Accessibility Checker: WCAG checks in Drupal Canvas
Check Drupal Canvas pages for accessibility issues as you edit, see which component causes each one, and jump to it. Built on Editoria11y's rules.
Get Canvas Accessibility Checker on Drupal.orgAn image component with no alt text looks fine in the Canvas preview. So does a button whose only content is an icon, or a form field with no label. Every one of them is a WCAG failure, and inside the Canvas editor nothing tells you. Editoria11y, the editor-facing checker many Drupal sites use, switches itself off inside the Canvas editor, so the first chance to catch the problem was after the page went live.
canvas_a11y (Canvas Accessibility Checker) closes that gap. It runs Editoria11y’s checks inside the editor, while you build, and tells you which component each issue belongs to.
What it looks like in the editor
The checker is a Canvas extension. Open it from the Extensions menu and a panel docks in the bottom-left corner of the editor. It lists every issue on the page as a tile, grouped by component and in the order the components appear on the page. The component you have selected is highlighted.
Each tile names the component type, describes the problem, and shows a suggested fix in green underneath, taken from Editoria11y’s own guidance. Click a tile and Canvas selects that component and scrolls it into view, so the settings panel on the right is already open on the thing you need to change.
Results update live. Fix the alt text and the tile disappears, with no refresh and no “run check” button. New issues are announced to screen readers as they arrive, and there is a reduced-motion version of the fix animation for editors who have that preference set.
The checks come straight from Editoria11y: heading structure, alt text, link and button names, colour contrast, and form labels. I added one rule of my own early on, a multiple-H1 check, and took it out again before 1.0.2. The panel now flags exactly what the site’s own Editoria11y flags, including any tests an administrator has switched off.
Ignore and Mark OK
Some alerts are judgement calls. Each tile has an X in its corner with the same two options Editoria11y offers: Ignore hides the alert for you, and Mark OK hides it for every editor. Both are stored by Editoria11y against the page, so a dismissal made in Canvas also holds on the front end. Hidden alerts sit in a list at the bottom of the panel where they can be restored, and an alert comes back on its own when the flagged content changes.
Permissions follow Editoria11y’s, so the options only appear for editors allowed to use them. The one place I went further: Editoria11y offers dismissal only for warnings, and this panel allows it for errors too.
Making it feel like part of Canvas
An extension that looks bolted on gets closed and forgotten, so a lot of the work went into the panel matching the editor around it. It loads Canvas’s own stylesheet and uses its theme tokens for colour, spacing, radius and type. An early version measured those styles before the stylesheet had finished loading and concluded the theme had not applied; the fix was waiting for the <link> to fire its load event before rendering anything.
Canvas does not remember which extension was open, so a refresh used to close the panel. The module remembers that in the browser and reopens it once the editor loads, keeping the Extensions sidebar out of sight while it does.
Tiles have visible focus, larger touch targets, and keep focus where it was when the list re-renders around them.
How an issue finds its component
Canvas wraps every component instance in its preview HTML with a pair of comments, canvas-start-{uuid} and canvas-end-{uuid}. Walking the document in order with a stack gives every element its innermost owning component, nested components included:
for (let node = walker.nextNode(); node; node = walker.nextNode()) { if (node.nodeType === Node.COMMENT_NODE) { const start = START.exec(node.data); const end = END.exec(node.data); if (start) stack.push(start[1]); if (end) stack.pop(); } else if (stack.length) { owner.set(node, stack.at(-1)); }}(Simplified: the real walker also tracks global regions and component metadata.)
Editoria11y then runs against a copy of that preview in an off-screen iframe, rendered at full size. Hiding it with display: none would drop layout, and contrast and visibility checks need real layout to mean anything. Each run gets a fresh iframe, too, because Editoria11y keeps a one-time initialization latch that never resets. A second run in the same window silently does nothing.
Two things that did not work
The Canvas Extensions API lets an extension read and subscribe to the current selection, but offers no way to set it. My first attempt dispatched a selection straight into Canvas’s Redux store. Canvas’s settings panel broke with an “unexpected error” toast, because the selection changed without the data fetch that normally comes with it. What works is pushing Canvas’s own .../component/{uuid} route and firing popstate, the same path a real click takes. It is undocumented, and a Canvas update that changes the route pattern would break it with nothing for the module to detect. I would much rather depend on a selection-write method in the Extensions API, and that is the change I would like to propose upstream.
The second failure was worse. With the Editoria11y library deliberately removed, the panel reported “No accessibility issues found on this page.” A false all-clear is the one result an accessibility checker must never produce, because it looks identical to a clean page. A load failure now shows “Couldn’t check this page” with a retry button, and I verified that by breaking and restoring the library path again.
Install
You need Drupal 11.2 or later, Drupal Canvas 1.4 or later, and Editoria11y 3.0.9 or later. Editoria11y is a hard dependency, so Composer pulls it in:
composer require 'drupal/canvas_a11y:^1.0'drush en canvas_a11y -yThere is no settings page. Open a page in Canvas, choose Accessibility from the Extensions menu, and the panel stays open across refreshes until you close it. After updating the module, clear Drupal’s caches as usual so editors load the new panel files.
The current release is 1.0.6, which strengthened the contrast of the tile outlines and added the screen reader announcements for new issues.
What it still does not do
Live checking runs only while the panel is open, because Canvas sends preview updates to an extension only while its dialog is showing. Opening it runs a fresh check.
Attribution has been verified for components in the content region. I have not yet confirmed it on a real site for global regions or Canvas content templates, and the global header I tested against is not a selectable component at all, so a tile there has nowhere to send you.
I also designed a heading-outline view and a short manual checklist for things no automated tool can judge, like captions and keyboard-only use, and built neither. The outline view is next. If you have content templates or global regions in Canvas and can tell me whether the tiles land on the right component, the issue queue is open.