Builder.io is a visual headless CMS that lets marketing and content teams drag, drop, and edit live components on top of an existing codebase (React, Next.js, Vue, Angular, Svelte, Qwik) and ship pages without a developer in the loop. Its newer AI and Figma-to-code features go further, generating whole layouts from a prompt or a design file. That power is exactly where the accessibility risk concentrates, and it splits into two layers that fail independently. The first is the registered custom components your developers expose to the visual editor: if a custom 'Accordion', 'Tabs', or 'Card' component is built without the correct ARIA and keyboard support, every editor who drags it onto a page inherits that defect, multiplied across the site. The second is what non-developer editors and the AI/Figma importers do inside the visual editor: choosing heading levels by visual size, dropping images without alt text, writing 'Learn more' on every button, and picking brand colors that fail contrast - none of which the drag-and-drop interface stops them from doing, and all of which ship straight to production because there is no code review step. Because Builder.io renders into a real front-end, the usual headless concerns about semantics and rendering apply too. With the European Accessibility Act now enforceable for businesses serving EU consumers, and ADA Title III and Section 508 obligations applying across Builder.io's large base of marketing and ecommerce sites in the United States, both the component library and the editor workflow are in scope. This checklist covers the issues we see most often on Builder.io sites and the specific component-registration settings, editor practices, and rendering targets that fix each one. None of this is legal advice; consult a qualified attorney for your jurisdiction.

Common Accessibility Issues

critical

Registered Custom Components Ship Without ARIA or Keyboard Support

WCAG 4.1.2

Developers register custom components (Accordion, Tabs, Carousel, Modal, Dropdown) so editors can drop them into pages visually. If those components are built without correct roles, state (aria-expanded, aria-selected), labels, and keyboard handling, the defect is inherited by every page that uses them. One inaccessible registered Tabs component can fail WCAG across an entire marketing site at once.

How to fix:

Build accessibility into the component before you register it: real button elements for controls, correct roles (tablist/tab/tabpanel, dialog), aria-expanded/aria-selected/aria-controls, focus management for modals, and full keyboard support (Enter/Space/arrow keys/Escape). Test each component with a keyboard and a screen reader, then register the audited version. Treat the registered component library as the single highest-leverage place to get accessibility right.

Before
// Registered accordion: div toggles, no state, no keyboard
Builder.registerComponent(Accordion, { name: 'Accordion' });
const Header = ({ onClick, label }) => <div onClick={onClick}>{label}</div>;
After
const Header = ({ open, onToggle, label, id }) => (
  <button aria-expanded={open} aria-controls={id} onClick={onToggle}>{label}</button>
);
// Panel: <div id={id} role="region">...</div>; register only after a11y testing
critical

AI-Generated and Figma-Imported Layouts Break the Heading Outline

WCAG 1.3.1

Builder.io's AI generation and Figma import infer heading levels from font size and visual weight, not from document structure, so imported pages commonly have multiple H1s, skipped levels, and section titles that are styled text blocks rather than real headings. Editors then trust the polished result and publish it. Screen reader users who navigate by headings get an outline that does not match the page.

How to fix:

After generating or importing a layout, review every text block's heading setting: exactly one H1 (the main page title/hero), then H2 for sections and H3 for subsections with no skipped levels. In each text block, set the semantic tag explicitly and control size with styles. Convert styled 'fake heading' text blocks into real headings. Do not trust importer output - verify the structure before publishing.

Before
<!-- Figma import: two H1s + a styled non-heading -->
<h1>Summer Sale</h1>
<h1>Shop New Arrivals</h1>
<div class="text-2xl font-bold">Categories</div>
After
<h1>Summer Sale</h1>
<h2>Shop New Arrivals</h2>
<h2>Categories</h2>
critical

Image Blocks Published Without Alt Text

WCAG 1.1.1

The Image block and image fields in the Builder.io visual editor have an alt-text input, but it is optional and editors regularly leave it empty, especially on AI/Figma-imported pages where images arrive with no alt. The front-end then renders empty or filename-based alt, so informative images - product shots, banners with text, infographics - convey nothing to screen reader users.

How to fix:

Set meaningful alt text on every informative Image block in the editor, describing what the image communicates; for a banner with text baked in, reproduce that text as the alt or as real on-page text. Mark decorative images so they render alt="". In custom image components, make the alt prop required in the registration inputs (required: true) so the editor surfaces it as a mandatory field, and never fall back to the filename in code.

Before
// Registered image input optional; code falls back to filename
{ name: 'image', type: 'file' }
<img src={image} alt={filename} />
After
// Make alt a required input next to the image
{ name: 'image', type: 'file', required: true },
{ name: 'imageAlt', type: 'string', required: true }
<img src={image} alt={imageAlt} />
serious

Button and Link Blocks Reuse Generic Text Across the Page

WCAG 2.4.4

Builder.io editors and the AI importer reuse the same call-to-action wording, so a page ends up with several Button/Link blocks all reading 'Learn more', 'Shop now', or 'Get started'. Screen reader users listing links hear the same phrase repeatedly with no way to distinguish destinations, failing WCAG 2.4.4 Link Purpose, and icon-only link blocks sometimes have no accessible name at all.

How to fix:

Give each Button/Link block distinct, descriptive text that makes sense out of context ('Shop the summer sale', 'Read the size guide'). Where the visible label must stay short, expose an aria-label input on your custom button component and set it to name the destination. For icon-only links, require an accessible-name input. Review the whole page for duplicated CTA text after an AI or Figma import.

Before
<a href="/sale">Shop now</a>
<a href="/new">Shop now</a>
<a href="/cart"><svg></svg></a> <!-- no accessible name -->
After
<a href="/sale">Shop the summer sale</a>
<a href="/new">Shop new arrivals</a>
<a href="/cart" aria-label="View cart"><svg aria-hidden="true"></svg></a>
serious

Brand Colors and Text-Over-Image Blocks Fall Below Contrast Minimums

WCAG 1.4.3

Editors style text and buttons in the visual editor using brand colors and place text over full-bleed image blocks. Because they preview against the design's own background, light-gray body text, pale button fills below 4.5:1, and text over busy images (where contrast varies across the photo) are easy to miss and ship directly to production with no code review to catch them.

How to fix:

Check body text, link, and button colors with a contrast checker and correct them in the block styles; where possible, expose only contrast-safe brand colors as presets/themes in the editor so editors cannot pick a failing combination. For text over images, add a solid or semi-opaque overlay so contrast holds across the whole image, or move text onto a solid panel. Re-check hover and focus states.

Before
/* Pale button text on brand-tint fill */
.cta { color: #ffffff; background: #b7d4f5; } /* ~1.6:1 - fails */
After
/* Corrected to meet 4.5:1 */
.cta { color: #ffffff; background: #1257a3; } /* ~5.2:1 - passes */
serious

Custom Form Components Use Placeholder Labels and Unannounced Errors

WCAG 3.3.2

Form and input blocks registered for the visual editor often render field names as placeholder text rather than persistent, associated labels, and surface validation as a single general message rather than per-field errors tied to the input. Placeholder-only labels disappear on typing and are announced inconsistently, and unassociated errors leave screen reader users unsure which field failed.

How to fix:

In your registered form components, render a real, persistent label associated via for/id for every input, indicate required fields in text rather than color alone, associate each error with its field via aria-describedby, set aria-invalid on failed fields, and move focus to the first error on a failed submit. Expose label text as an editor input so editors set labels, not placeholders. Test the form with a keyboard and screen reader.

Before
<input type="email" placeholder="Email" /> <!-- placeholder as label -->
After
<label for="f-email">Email <abbr title="required">*</abbr></label>
<input id="f-email" type="email" required aria-required="true" aria-describedby="f-email-err" />
<span id="f-email-err" role="alert"></span>
moderate

Animation and A/B Test Variants Ignore Reduced-Motion and Vary Accessibility

WCAG 2.3.3

Builder.io supports entrance animations and built-in A/B testing and personalization, so different visitors can receive different page variants. Animations often run regardless of the operating-system reduce-motion setting, and accessibility fixes applied to one variant (alt text, heading structure, contrast) are easy to forget on the others, so a B variant or a personalized version can fail even when the control passes.

How to fix:

Respect prefers-reduced-motion in your animation styles or component code so entrance/scroll motion is disabled when reduced motion is requested. When you A/B test or personalize, audit every variant for headings, alt text, link purpose, and contrast - not just the control - and treat accessibility as a release gate for each variant, since users are randomly assigned to them.

Before
/* Animation applied to all visitors, all variants */
.builder-block { transform: translateY(40px); transition: transform .5s; }
After
@media (prefers-reduced-motion: reduce) {
  .builder-block { transform: none !important; animation: none !important; transition: none !important; }
}
/* Plus: audit each A/B variant for a11y before launch */

Builder.io-Specific Tips

  • Get the registered component library right first - it is the highest-leverage place for accessibility. An audited Accordion/Tabs/Modal protects every page an editor builds with it; a broken one fails them all.
  • Never publish AI-generated or Figma-imported layouts unreviewed. Importers infer headings from font size, drop images without alt text, and duplicate CTA wording - review headings, alt text, link text, and contrast on every imported page.
  • Make alt and accessible-name props required (required: true) in your custom component registrations so the visual editor surfaces them as mandatory fields editors cannot skip.
  • Expose only contrast-safe brand colors as presets/themes in the editor so non-developer editors cannot select a combination that fails 4.5:1 or 3:1.
  • Render form labels as persistent, associated labels driven by an editor-set label input, not placeholder text, and tie validation errors to fields with aria-describedby.
  • Audit every A/B test and personalization variant for accessibility, not just the control, and respect prefers-reduced-motion - since visitors are randomly assigned to variants, each one must pass.

axe DevTools

Browser extension and CI tool for automated WCAG testing. Run it on the published Builder.io front-end with registered components, imported layouts, and each A/B variant in place to catch missing alt text, broken headings, and unlabelled controls.

Builder.io custom component inputs (registerComponent)

Use the input configuration (required, type, defaults) to make alt text and accessible-name fields mandatory and to expose only contrast-safe color presets, building accessibility constraints into the editor experience itself.

WebAIM Contrast Checker

Check brand colors, button fills, link colors, and text over image blocks against the 4.5:1 and 3:1 minimums before exposing colors as editor presets or publishing a page.

NVDA + Firefox / VoiceOver + Safari

Manual screen-reader testing verifies that registered interactive components (tabs, accordions, modals), imported layouts, and forms announce roles, state, and labels correctly - things automated scanners and AI import cannot judge.

Lighthouse (Chrome DevTools)

Built-in Chrome audit that catches common Builder.io issues like low contrast, missing alt text, and missing accessible names. Treat a high score as necessary but not sufficient and pair it with manual keyboard and screen-reader testing.

Builder.io Accessibility At a Glance

Plugin / Tool AreaCommon FailureWCAGBest Fix
Registered Components tabs, accordion, modal No ARIA/state/keyboard support4.1.2Audit & harden before registering
AI / Figma Import generated layouts Multiple H1s, broken outline1.3.1Review headings before publishing
Image Blocks editor + import Optional alt left blank1.1.1Make alt input required; set meaningful alt
Button / Link Blocks calls to action Repeated 'Shop now' / icon-only links2.4.4Distinct text; required accessible name
A/B Variants testing/personalization Only control audited; motion ignored2.3.3Audit every variant; respect reduced-motion

Frequently Asked Questions

Where do accessibility problems come from on a Builder.io site?

Two layers that fail independently. The first is the registered custom components your developers expose to the visual editor: if a custom Accordion, Tabs, Modal, or Carousel is built without correct roles, state, labels, and keyboard support, that defect is inherited by every page an editor builds with it. The second is what non-developer editors and the AI/Figma importers do inside the editor with no code review step - choosing heading levels by size, dropping images without alt text, reusing 'Learn more' on every button, and picking brand colors that fail contrast. The fix mirrors the two layers: audit and harden the registered component library first (it is the highest-leverage place), then constrain and review the editor workflow - make alt and accessible-name inputs required, expose only contrast-safe color presets, and review every imported or AI-generated page before publishing.

Are AI-generated or Figma-imported Builder.io pages accessible out of the box?

No, not reliably. Both the AI generator and the Figma import infer structure from visual appearance, so they routinely produce multiple H1s and broken heading outlines, images with no alt text, styled text blocks standing in for real headings, and duplicated call-to-action wording - none of which is visible in the polished preview. Because the output looks finished and there is no code review step in the visual workflow, editors tend to publish it as-is. Builder.io can produce a conformant page, but only if you review every imported or generated layout for heading structure, alt text, link purpose, form labels, and contrast before launch, and test the live page with a keyboard and a screen reader. Treat importer output as a draft that must pass an accessibility check, not a finished page.

How do I stop editors from publishing inaccessible pages in Builder.io?

Build the guardrails into the editor itself rather than relying on training alone. In your custom component registrations, make alt-text and accessible-name inputs required (required: true) so editors must fill them, expose only contrast-safe brand colors as presets/themes so a failing color combination is not selectable, and render persistent associated labels (driven by an editor-set label input) instead of placeholders in form components. Ship only registered interactive components that have already passed keyboard and screen-reader testing, so editors cannot drop an inaccessible accordion or modal onto a page. Then add an automated axe check in CI against published pages and each A/B variant. Constraining the building blocks is far more reliable than asking every editor to remember accessibility, and it is general guidance, not legal advice.

Further Reading

Other CMS Checklists