Drupal has one of the strongest accessibility track records of any CMS. Accessibility is a formal gating requirement for core: patches that introduce WCAG violations are not committed, and core ships ARIA support, semantic form output, and a screen-reader announcement API out of the box. That head start is real, but it does not make your site compliant. The theme you build, the content editors publish, and the contrib modules you bolt on all decide whether a finished Drupal site actually meets the bar.
This guide is for Drupal site builders who need to reach WCAG 2.2 Level A and AA — the baseline referenced by EN 301 549 and required under the European Accessibility Act. It covers what core gives you, which themes and modules to lean on, how to keep alt text and forms accessible, and the checks no automated tool can do for you.
What WCAG 2.2 AA and the EAA require of a Drupal site
The legal baseline is WCAG 2.2 Level A and AA, referenced through EN 301 549. The European Accessibility Act (Directive (EU) 2019/882) has applied to in-scope products and services since 28 June 2025, and the earlier EU Web Accessibility Directive (2016/2102) already binds public-sector bodies — many of which run Drupal. If you sell to EU consumers or you are a public body, you are likely in scope. Microenterprises providing services may be partly exempt, but the carve-out is narrow, so confirm your scope rather than assume it.
WCAG rests on four principles: content must be Perceivable, Operable, Understandable, and Robust. You do not need to memorise every success criterion. On a typical Drupal site, a short list of failures causes most violations: empty or unhelpful alt text, broken heading order from nested view modes, unlabelled exposed filters and Webform elements, low colour contrast in a custom theme, and keyboard traps in JavaScript widgets. Fix those and you are most of the way to AA.
WCAG 2.2 added seven success criteria, six of which apply at Level A or AA: 2.4.11 Focus Not Obscured (Minimum, AA), 2.5.7 Dragging Movements (AA), 2.5.8 Target Size Minimum (AA), 3.2.6 Consistent Help (A), 3.3.7 Redundant Entry (A), and 3.3.8 Accessible Authentication Minimum (AA). (The seventh, 2.4.13 Focus Appearance, is AAA.) WCAG 2.2 also removed 4.1.1 Parsing, so you no longer fail purely on duplicate IDs or invalid nesting — though Drupal's render system can still emit those, and they cause real screen-reader bugs worth fixing.
Contrast minimums are unchanged: 4.5:1 for normal text, and 3:1 for large text (at least 18pt, or 14pt bold) and for UI component boundaries and graphical objects (WCAG 1.4.3 and 1.4.11). Build your custom theme palette to clear those ratios before you ship.
What Drupal core gives you for free
Core handles a surprising amount of the markup correctly when you let it. The Form API renders proper label/input associations, wraps radio and checkbox groups in fieldset and legend, and exposes #description text via aria-describedby. Core JavaScript ships Drupal.announce(), a polite ARIA live-region helper used by AJAX, the autocomplete widget, and form validation so screen readers hear dynamic updates. The off-canvas tray, modal dialogs, and the toolbar all manage focus and expose ARIA states.
- Skip links: core outputs a 'Skip to main content' link as the first focusable element — keep it in custom themes.
- Semantic regions: core templates use header, nav, main, and footer landmarks; do not flatten them into div soup when you re-theme.
- Tabledrag and tableselect: core's draggable tables expose keyboard handles, which matters for WCAG 2.5.7 Dragging Movements — preserve them rather than swapping in a mouse-only library.
- Language and direction: core sets lang and dir on the html element from the site/content language, satisfying 3.1.1 Language of Page.
The catch is that core only helps if your theme and modules build on its render arrays instead of bypassing them. Hard-coded HTML in a Twig template, a custom block that prints unlabelled inputs, or a contrib widget that ignores Drupal.announce() all throw away core's work.
Themes: Olivero, Claro, and custom builds
Olivero, the front-end theme that ships as the default in recent Drupal releases, was built with accessibility as an explicit goal: it ships a keyboard-operable dropdown menu, visible focus styles, sufficient contrast in its default palette, and a working skip link. Claro, the admin theme, was similarly designed for editors who use assistive technology. Starting from either gives you a defensible baseline.
Most real projects use a custom or sub-theme, and that is where regressions creep in. The common failures: removing focus outlines for aesthetics (a 2.4.7 fail), building a mega-menu that only opens on hover (a keyboard and 2.5.7 problem), shipping a colour palette that looks good but fails 4.5:1, and overriding Twig templates in ways that drop ARIA attributes or break heading order. If you base a theme on a third-party HTML template, audit it — many premium templates are not accessible out of the box.
Two manual checks pay for themselves on every theme: tab through the entire page with the keyboard alone and confirm focus is always visible and never trapped, and operate the menu, search, and any carousel without a mouse. Pair those with checks for focus order, target size, and reflow at high zoom before you call a theme done.
Contrib modules worth installing
Contrib can catch issues editors would otherwise miss, but choose carefully — automated tools only flag a fraction of WCAG criteria.
- Editoria11y: an in-context checker that flags missing alt text, skipped headings, suspicious link text, and contrast problems directly on the rendered page, where editors actually see them. Strong choice for content teams.
- CKEditor 5 Accessibility Checker (and the built-in editor affordances): helps editors catch issues in body fields as they write, including image alt prompts and table headers.
- Accessibility helper modules: add targeted fixes such as form-element enhancements, though always verify they still match current core markup before relying on them.
- Block ARIA Landmark Roles: lets site builders assign roles and aria-label to blocks so regions are distinguishable to screen-reader users.
Avoid overlay or 'accessibility widget' modules that inject a floating toolbar promising one-click compliance. They do not fix the underlying HTML, cannot satisfy WCAG 2.2 AA or the EAA on their own, and are widely criticised by screen-reader users. Spend the effort on clean markup instead.
Alt text and forms — the two things editors break most
Drupal's Image field makes the Alt text box required by default, which is the right call. But required does not mean useful: editors will type the filename or repeat the caption just to clear the field. Train them that alt text describes the function of the image in context, and that purely decorative images should get an explicitly empty alt (alt="") so screen readers skip them. The Media Library stores alt on the media entity, so a reused image carries its alt everywhere — good for consistency, but a poor description then propagates site-wide.
For forms, the Form API gives you accessible output if you use it properly: set a real #title on every element, use #title_display rather than removing the label, and put help text in #description so it is announced. The danger zone is Webform and Views exposed filters. In Webform, never leave an element relying on placeholder text as its only label, group related radios with a fieldset, and mark required fields with #required (which adds the programmatic indication, not just a red asterisk). For exposed Views filters, give each filter an explicit, non-empty label even when the theme visually hides it.
What you still have to verify manually
Automated checks — including AccessScan — reliably catch missing alt attributes, many contrast failures, missing form labels, and empty links. They cannot judge whether alt text is meaningful, whether focus order makes sense, or whether a custom widget is operable by keyboard. Reserve human testing for those.
- Keyboard-only pass: complete a full journey (menu, search, a Webform submission, checkout if you run Commerce) using only Tab, Shift+Tab, Enter, Space, and arrow keys.
- Screen-reader spot check: read a content page, a form, and any AJAX-updated view with VoiceOver or NVDA to confirm labels, headings, and live announcements make sense.
- Focus visibility and obscuring (2.4.11): confirm a sticky header or cookie banner never hides the focused element.
- Target size (2.5.8): check that icon-only buttons and pagination links meet the 24x24 CSS pixel minimum.
- Reflow and zoom: at 400% zoom, content should not require horizontal scrolling and nothing should overlap.
Run an automated scan first to clear the obvious failures, then do the manual passes above. You can start a free scan at AccessScan, and once you have evidence of conformance, publish a statement with the accessibility statement generator.