Elementor is the most widely used page builder for WordPress, powering a very large share of small-business, agency, and freelancer-built sites. Its drag-and-drop canvas, hundreds of widgets, and the Elementor Pro add-ons (Theme Builder, Popup Builder, Forms) let people who do not write code assemble sophisticated pages quickly. That same flexibility is where accessibility goes wrong: because the builder lets you choose how every element looks independently of what it actually is, non-developers routinely make design choices that confuse screen readers, trap keyboard users, and fail color-contrast minimums without any visible warning. The widget that sets a heading lets you pick the visual size and the HTML tag separately, so pages end up with several H1s or skipped levels. The carousel and slides widgets autoplay by default. The Popup Builder shows a dialog without trapping focus inside it. None of this is obviously broken to a sighted mouse user, which is exactly why it survives to production. Elementor has added accessibility-related options over the years (an experiments panel with optimized markup and improved CSS-loading flags, ARIA controls on some widgets, and a separate 'Ally' accessibility toolbar widget), but those features do not fix the underlying markup choices an editor makes inside the canvas, and a toolbar widget is not a substitute for accessible structure. With the European Accessibility Act now enforceable for businesses selling to EU consumers, and ADA Title III litigation in the US continuing to target small-business websites, Elementor site owners need to treat accessibility as a build-time decision rather than a plugin you bolt on at the end. This checklist walks through the failures we see most often on Elementor sites and gives the specific Elementor Editor steps and markup targets to fix each one.

Common Accessibility Issues

critical

Heading Widget HTML Tag Chosen by Visual Size, Breaking Document Outline

WCAG 1.3.1

Elementor's Heading widget exposes the visual size (Small/Medium/Large/XL/XXL) and the HTML tag (H1-H6, div, span, p) as two independent settings. Editors pick the tag that looks the right size rather than the one that fits the page outline. The result is pages with multiple H1 elements (the page title plus a hero heading), skipped levels (an H2 followed directly by an H4 because H4 looked better), and section titles marked as plain div or span so they vanish from screen-reader heading navigation entirely. Screen reader users who navigate by pressing 'H' to jump between headings get a broken, confusing structure.

How to fix:

Decide the page outline first: exactly one H1 (usually the page or post title coming from your Theme Builder template, so set hero headings to H2), then nested H2/H3/H4 in reading order with no skipped levels. In each Heading widget, open the Content tab and set the 'HTML Tag' field to match the outline, then use the Style tab (Typography > Size) to control how big it looks. Never use a div or span for a real heading. If your Theme Builder single/page template already outputs an H1 for the title, audit your hero section so it does not add a second H1.

Before
<!-- Two H1s and a skipped level produced by picking tags for size -->
<h1 class="elementor-heading-title">Welcome to Acme</h1>   <!-- hero, duplicates the page-title H1 -->
<h1 class="elementor-heading-title">Our Services</h1>
<h4 class="elementor-heading-title">Web Design</h4>   <!-- jumps H1 -> H4 -->
After
<!-- One H1 (page title), logical nesting, size controlled by CSS -->
<h1 class="elementor-heading-title">Acme Web Studio</h1>
<h2 class="elementor-heading-title">Our Services</h2>
<h3 class="elementor-heading-title">Web Design</h3>
critical

Image Carousel and Slides Widgets Autoplay With No Pause Control

WCAG 2.2.2

Elementor's Image Carousel, Slides, Media Carousel, and Testimonial Carousel widgets (built on Swiper) default to autoplay with a fixed interval. Content that moves, blinks, or auto-advances for more than five seconds must offer a way to pause, stop, or hide it. Autoplaying carousels also create problems for screen reader users (content changes under them) and for people with cognitive disabilities or attention-related disabilities who cannot read a slide before it moves.

How to fix:

In the carousel/slides widget settings, open the Content tab and either set Autoplay to 'No', or if marketing insists on autoplay, ensure a visible, keyboard-operable pause/play control is present and pair it with 'Pause on Interaction' and 'Pause on Hover'. Confirm the navigation arrows and pagination dots are reachable by Tab and operable with Enter/Space, and that each arrow has an accessible name. The safest, most accessible choice for most small-business pages is to replace an autoplaying carousel with a static hero or a manually advanced gallery.

Before
<!-- Swiper autoplay on, no pause control rendered -->
<div class="swiper" data-settings='{"autoplay":{"delay":3000},"pause_on_hover":"no"}'>
After
<!-- Autoplay off, or pause control + pause-on-hover/interaction enabled -->
<div class="swiper" data-settings='{"autoplay":"no"}'>
<!-- If autoplay is required, add a visible button: -->
<button type="button" class="carousel-pause" aria-label="Pause slideshow">Pause</button>
critical

Popup Builder Dialogs Do Not Trap or Return Focus

WCAG 2.4.3

Elementor Pro's Popup Builder is used for newsletter sign-ups, discount offers, and cookie/age gates. By default a popup appears visually on top of the page, but keyboard focus is not moved into it, focus is not trapped while it is open, the Escape key may not close it, and when it closes focus is not returned to the element that opened it. Keyboard and screen reader users can end up interacting with the page behind the popup, or get lost entirely, and a modal that cannot be dismissed from the keyboard can block the whole page.

How to fix:

Treat every Elementor popup as a modal dialog. Move focus to the first interactive element (or the dialog container) when it opens, keep Tab focus cycling inside it until it closes, allow Escape to close it, and return focus to the trigger afterward. Elementor's popup settings do not fully implement this, so add custom JavaScript via a Custom Code snippet (Elementor > Custom Code, or a code-snippets plugin) that wires up the focus trap, Escape handling, and focus return, and add role="dialog" with aria-modal="true" and an aria-label to the popup container. Avoid popups that block the page on entry where you can.

Before
<!-- Popup shown but not announced or trapped -->
<div class="elementor-popup-modal" style="display:block">...</div>
After
<div class="elementor-popup-modal" role="dialog" aria-modal="true" aria-label="Newsletter sign-up">
  <button type="button" class="dialog-close" aria-label="Close dialog">x</button>
  ...
</div>
<!-- JS: on open move focus in + trap; Escape closes; on close restore focus to trigger -->
serious

Accordion, Toggle, and Tabs Widgets Without Correct ARIA or Keyboard Support

WCAG 4.1.2

Older Elementor Accordion, Toggle, and Tabs widgets render headers as non-button elements with click handlers, so screen readers do not announce them as expandable controls or report whether each section is open or collapsed, and keyboard users cannot always operate them. Newer Elementor versions ship a 'Nested Accordion' and 'Nested Tabs' that use proper button semantics and ARIA, but many live sites still run the legacy widgets, and a site can mix both.

How to fix:

Prefer Elementor's newer Nested Accordion and Nested Tabs widgets, which expose accordion headers as buttons with aria-expanded and tabs with the role="tablist"/role="tab"/role="tabpanel" pattern and arrow-key navigation. Replace legacy Accordion/Toggle/Tabs instances during your next content pass. After replacing, test with the keyboard: each accordion header should toggle with Enter and Space and expose its expanded state, and tabs should move with Left/Right arrow keys with Home/End jumping to first/last. Confirm with a screen reader that the state changes are announced.

Before
<!-- Legacy accordion header is a div, no state exposed -->
<div class="elementor-accordion-title" tabindex="0">Shipping & Returns</div>
After
<!-- Nested Accordion header is a real button reporting its state -->
<button class="elementor-accordion-title" aria-expanded="false" aria-controls="acc-1">
  Shipping &amp; Returns
</button>
<div id="acc-1" role="region" aria-labelledby="...">...</div>
serious

Icon Box and Icon List Decorative Icons Announced; Icon-Only Links Have No Name

WCAG 1.1.1

The Icon Box, Icon List, and Social Icons widgets place icons (rendered as inline SVG or icon fonts) throughout a page. Decorative icons that sit next to a text label are often still exposed to screen readers, adding noise. The opposite problem is worse: icon-only links and buttons (a bare social icon, a search or cart icon with no text) provide no accessible name, so a screen reader announces 'link' or 'button' with no indication of what it does.

How to fix:

For purely decorative icons that accompany visible text, ensure they are hidden from assistive technology (aria-hidden="true" on the icon, which Elementor applies in most current widgets, decorative SVGs should also have focusable="false"). For icon-only links and buttons, add an accessible name: in the widget's settings use the link/aria-label option where available, or add a visually-hidden text label. Social Icons should have the network name as their accessible name (for example 'Acme on Instagram', not just the icon).

Before
<!-- Icon-only link, no accessible name -->
<a href="https://instagram.com/acme"><i class="fab fa-instagram"></i></a>
After
<a href="https://instagram.com/acme" aria-label="Acme on Instagram">
  <i class="fab fa-instagram" aria-hidden="true"></i>
</a>
serious

Elementor Pro Form Fields Labelled Only by Placeholder; Errors Not Associated

WCAG 3.3.2

Elementor Pro Forms let editors hide field labels and rely on placeholder text instead. Placeholder text disappears as soon as the user types, leaves no label for screen readers in some browsers, and frequently fails contrast because placeholder gray is light. When validation fails, Elementor shows a general message; field-level errors are not always programmatically tied to the input with aria-describedby, and focus is not moved to the first error.

How to fix:

In each form field's settings, keep the Label enabled and set it to a clear text label (you can position it above the field). Do not rely on the placeholder as the only label. Mark required fields with required and ensure the required indicator is announced, not just shown with a colored asterisk. For errors, associate each message with its field via aria-describedby and set aria-invalid on the failed input; if your version does not do this automatically, add a Custom Code snippet to wire error messages to fields and move focus to the first invalid field on submit failure.

Before
<!-- Label hidden, placeholder doing double duty -->
<input type="email" placeholder="Email address" required>
After
<label for="form-email">Email address <abbr title="required">*</abbr></label>
<input type="email" id="form-email" required aria-required="true"
       aria-invalid="false" aria-describedby="form-email-error">
<span id="form-email-error" role="alert"></span>
serious

Motion Effects and Entrance Animations Ignore Reduced-Motion Preferences

WCAG 2.3.3

Elementor's Motion Effects (scrolling and mouse-track parallax, transforms, blurs) and per-widget entrance animations run for all visitors regardless of the operating-system 'reduce motion' setting. Large parallax movement, sliding entrances, and looping animations can trigger nausea, dizziness, and disorientation for people with vestibular disorders.

How to fix:

Add a prefers-reduced-motion media query in Elementor's Custom CSS (Site Settings > Custom CSS, or per-page) that disables transforms, parallax, and entrance animations when the visitor has requested reduced motion. Keep animations subtle and opacity-based rather than large movements. Background videos should have a visible pause control and a static fallback for reduced-motion users.

Before
/* Parallax + entrance animations applied to everyone */
.elementor-motion-effects-element { transform: translateY(120px); }
After
@media (prefers-reduced-motion: reduce) {
  .elementor-motion-effects-element,
  .elementor-invisible,
  [data-settings*="animation"] {
    transform: none !important;
    opacity: 1 !important;
    animation: none !important;
    transition: none !important;
  }
}
moderate

Global Colors and Layout Kits Produce Text and Buttons Below 4.5:1 Contrast

WCAG 1.4.3

Elementor's Global Colors and the imported Template/Kit designs often use light gray body text, pale buttons, and low-contrast link colors that look modern but fall below the 4.5:1 ratio required for normal-size text (3:1 for large text and meaningful UI components). Because the editor previews on a clean white canvas, these failures are easy to miss, and a single Global Color value propagates the problem across the whole site.

How to fix:

Check every Global Color pairing (text on background, button text on button fill, link color on background) with a contrast checker and adjust the Global Color values so they meet at least 4.5:1 for body text and 3:1 for large text and UI components. Fixing it at the Global Colors level updates the whole site at once. Pay special attention to buttons in hover/focus states and to text placed over background images or color overlays.

Before
/* Global text color too light on white */
--e-global-color-text: #9aa0a6; /* ~2.6:1 on #fff - fails */
After
/* Adjusted to pass body-text contrast */
--e-global-color-text: #4b5563; /* ~7:1 on #fff - passes */
--e-global-color-accent: #b91c1c; /* button fill, check text-on-fill too */

Elementor-Specific Tips

  • Set your H1 once: most Elementor sites get the page/post title H1 from a Theme Builder Single or Archive template. Decide there whether the title is the H1, then keep hero Heading widgets at H2 so you never ship a page with two H1s.
  • Open Elementor > Settings > Features (Experiments) and enable the 'Optimized Markup' and improved CSS/loading options. They produce cleaner, lighter HTML, which is a better foundation for accessibility, though they do not fix editor-level choices like heading tags or contrast.
  • Elementor's free 'Ally' accessibility widget adds a front-end toolbar (text resize, contrast toggles). Treat it as a convenience for users, not a compliance fix: a toolbar cannot repair missing labels, broken heading order, or untrapped popups, and overlay-style 'one-click compliance' claims do not hold up in audits or litigation.
  • Replace legacy Accordion, Toggle, and Tabs widgets with the newer Nested Accordion and Nested Tabs, which ship proper button semantics, aria-expanded, and the tablist keyboard pattern. Mixing old and new on one site leads to inconsistent screen-reader behavior.
  • Audit popups separately from pages. Every Popup Builder template needs role="dialog", aria-modal, focus moved in on open, a focus trap, Escape to close, and focus returned to the trigger. Test each popup with the keyboard only before publishing.
  • Test with the keyboard and a screen reader on the published page, not just in the editor. The Elementor canvas hides real tab order, autoplay behavior, and focus problems that only appear on the live front-end.

axe DevTools

Browser extension that scans the published Elementor page for WCAG violations with specific remediation guidance. Run it on the live front-end after popups, carousels, and forms are in place, not on the editor preview.

WAVE by WebAIM

Visual evaluator that overlays icons on the rendered page to flag missing alt text, empty links, contrast failures, and heading-order problems. Useful for showing non-technical site owners exactly which Elementor widget is at fault.

WebAIM Contrast Checker

Check every Elementor Global Color pairing (text, buttons, links) against the 4.5:1 and 3:1 thresholds. Fixing values at the Global Colors level corrects contrast across the whole site at once.

NVDA + Firefox / VoiceOver + Safari

Manual screen-reader testing is the only reliable way to verify heading announcements, popup focus handling, accordion/tab state, and carousel behavior on an Elementor site. Automated scanners cannot judge announcement quality.

Lighthouse (Chrome DevTools)

Built into Chrome DevTools; runs an automated accessibility audit and flags common Elementor issues like low contrast and missing names. A high score is necessary but not sufficient, pair it with manual testing.

Further Reading

Other CMS Checklists