Extension Accessibility Checklist
Complete WCAG 2.1 AA accessibility checklist for Chrome extensions — keyboard navigation, screen readers, contrast, ARIA, focus management, and testing with assistive technologies.
Table of Contents
Extension Accessibility Checklist
WCAG 2.1 AA compliance for every extension surface — popup, options page, side panel, and content scripts.
Accessibility is not a feature. It is a quality bar. An extension that 15% of users cannot operate — because they use a screen reader, navigate by keyboard, have low vision, or experience motor limitations — is a broken extension that happens to work for the majority.
Chrome extensions present unique accessibility challenges that web applications do not. Popups are small, ephemeral surfaces where focus management matters enormously. Options pages are forms that must work without a mouse. Content scripts inject UI into pages with unpredictable DOM structures and styling. Side panels maintain persistent state while the user interacts with a completely different page.
This guide is a working checklist. Print it, bookmark it, or integrate it into your pull request template. Every item is actionable and testable.
15-20%
Users with Disabilities
WHO estimate of global population with some form of disability
~7%
Keyboard-Only Users
Including power users who prefer keyboard navigation
~2%
Screen Reader Users
Using JAWS, NVDA, VoiceOver, or ChromeVox
~8% of men
Color Vision Deficiency
Red-green deficiency most common; affects UI color choices
Accessibility Audit Process#
Before diving into individual checklist items, understand the systematic process for auditing your extension.
Automated Scan
Run axe DevTools or Lighthouse accessibility audit on every extension page (popup, options, side panel). Automated tools catch about 30% of accessibility issues — things like missing alt text, insufficient contrast, and missing ARIA labels.
Keyboard Navigation Test
Unplug your mouse. Navigate your entire extension using only Tab, Shift+Tab, Enter, Space, Arrow keys, and Escape. Every interactive element must be reachable and operable. Focus order must be logical.
Screen Reader Walkthrough
Turn on ChromeVox (built into Chrome OS) or VoiceOver (macOS). Navigate your extension as a screen reader user would. Listen to what is announced. Does it make sense without seeing the screen?
Zoom and Reflow Test
Zoom to 200% in your popup and options page. Does content reflow properly? Is anything cut off or overlapping? WCAG requires content to be usable at 200% zoom.
Color-Only Information Test
View your extension in grayscale (use a CSS filter or OS accessibility setting). Can you still understand every piece of information? Status indicators, error states, and success messages must not rely solely on color.
Motion and Animation Test
Enable 'Reduce Motion' in your OS settings. Do all animations respect prefers-reduced-motion? Are there any auto-playing animations that could trigger vestibular disorders?
Checklist by Extension Context#
Different extension surfaces have different accessibility requirements. Use the appropriate section for each part of your UI.
Screen Reader Compatibility#
Not all screen readers behave identically. The major ones have meaningful differences in how they interpret ARIA and handle dynamic content.
| Feature | Feature | JAWS (Windows) | NVDA (Windows) | VoiceOver (macOS) | ChromeVox |
|---|---|---|---|---|---|
| Market share | ~40% | ~30% | ~15% | ~5% | |
| ARIA support | Excellent | Excellent | Good | Good | |
| Shadow DOM | Partial | Partial | Good | Best | |
| Live regions | Reliable | Reliable | Sometimes delayed | Reliable | |
| Custom elements | Needs ARIA roles | Needs ARIA roles | Better native support | Best support | |
| Cost | $1000+ license | Free / open source | Free (built-in) | Free (built-in) |
ChromeVox deserves special attention for Chrome extension testing because it is the screen reader most likely to be used inside Chrome. It understands Chrome-specific UI patterns better than other screen readers and handles Shadow DOM more consistently. Test with ChromeVox first, then verify with at least one other screen reader.
ARIA Patterns for Extensions#
Here are the ARIA patterns most commonly needed in Chrome extension UI.
Accessible Toggle Button#
// Toggle button with proper ARIA state
function createToggle(label: string, initialState: boolean): HTMLButtonElement {
const button = document.createElement("button");
button.setAttribute("role", "switch");
button.setAttribute("aria-checked", String(initialState));
button.setAttribute("aria-label", label);
button.textContent = initialState ? "On" : "Off";
button.addEventListener("click", () => {
const current = button.getAttribute("aria-checked") === "true";
const next = !current;
button.setAttribute("aria-checked", String(next));
button.textContent = next ? "On" : "Off";
});
// Keyboard: Space and Enter both toggle
button.addEventListener("keydown", (e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
button.click();
}
});
return button;
}Live Status Region#
<!-- Announce dynamic status changes to screen readers -->
<div id="status" role="status" aria-live="polite" aria-atomic="true">
<!-- Content updated dynamically -->
</div>
<script>
function updateStatus(message: string): void {
const status = document.getElementById("status")!;
// Clear first, then set — ensures re-announcement of same message
status.textContent = "";
requestAnimationFrame(() => {
status.textContent = message;
});
}
// Usage:
updateStatus("Settings saved successfully");
updateStatus("3 items selected");
updateStatus("Loading complete — 42 results found");
</script>Focus Trap for Modal Dialogs#
function trapFocus(container: HTMLElement): () => void {
const focusable = container.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
function handler(e: KeyboardEvent): void {
if (e.key !== "Tab") return;
if (e.shiftKey) {
if (document.activeElement === first) {
e.preventDefault();
last.focus();
}
} else {
if (document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}
container.addEventListener("keydown", handler);
first.focus();
// Return cleanup function
return () => container.removeEventListener("keydown", handler);
}Common Mistakes#
- Use semantic HTML elements (button, a, input) instead of div/span with click handlers
- Provide text alternatives for every non-decorative image (alt text or aria-label)
- Ensure focus indicators are visible — never use outline: none without a replacement
- Test with actual screen readers, not just automated tools
- Support both mouse and keyboard for every interaction
- Use rem/em units for font sizes so they respect user zoom preferences
- Use div or span as buttons — screen readers announce them as generic text, not interactive elements
- Hide content with display: none when it should be available to screen readers — use visually-hidden CSS instead
- Use title attributes as the only accessible label — most screen readers ignore them
- Rely on color alone to communicate state (red for error, green for success)
- Use tabindex values greater than 0 — they create unpredictable tab ordering
- Auto-focus elements on page load without user expectation — it disorients screen reader users
Compliance Progress Tracking#
Use these areas to track your extension's WCAG 2.1 AA compliance. Each area should be tested independently.
These four principles — Perceivable, Operable, Understandable, and Robust (POUR) — are the foundation of WCAG. Every specific criterion maps to one of these principles. When you are unsure whether something is an accessibility issue, ask: "Can all users perceive this? Operate it? Understand it? Access it with their assistive technology?"
Test Your Knowledge#
1. What is the minimum contrast ratio required by WCAG 2.1 AA for normal-sized text?
2. Why should you avoid using tabindex values greater than 0?
3. What happens with aria-labelledby references across Shadow DOM boundaries?
4. What is the minimum touch target size recommended by WCAG?
Automated Testing Integration#
Integrate accessibility checks into your build pipeline so regressions are caught before they ship.
// playwright.config.ts — add accessibility tests to E2E suite
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";
test("popup meets WCAG 2.1 AA", async ({ page }) => {
// Load the popup page
await page.goto(`chrome-extension://${EXTENSION_ID}/popup.html`);
// Run axe accessibility scan
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
// Fail the test if any violations are found
expect(results.violations).toEqual([]);
});
test("options page keyboard navigation", async ({ page }) => {
await page.goto(`chrome-extension://${EXTENSION_ID}/options.html`);
// Tab through all interactive elements
const focusOrder: string[] = [];
for (let i = 0; i < 20; i++) {
await page.keyboard.press("Tab");
const focused = await page.evaluate(() => {
const el = document.activeElement;
return el ? `${el.tagName.toLowerCase()}${el.id ? "#" + el.id : ""}` : "none";
});
focusOrder.push(focused);
if (focused === "none" || focused === "body") break;
}
// Verify logical focus order (customize for your UI)
expect(focusOrder[0]).toContain("input"); // Search field first
expect(focusOrder).not.toContain("none"); // No focus traps
});Checklist
- Every interactive element is operable via keyboard (Tab, Enter, Space, Escape)
- Focus order follows a logical sequence matching visual layout
- Focus indicators are visible in both light and dark themes
- All images have appropriate alt text (or alt='' for decorative images)
- Color is never the sole means of conveying information
- Text contrast meets 4.5:1 for normal text and 3:1 for large text
- Form inputs have associated labels (not just placeholders)
- Error messages are connected to inputs via aria-describedby
- Dynamic content updates are announced via aria-live regions
- Modal dialogs trap focus and return focus on close
- The extension respects prefers-reduced-motion for animations
- Content is usable at 200% browser zoom without horizontal scrolling
- Screen reader testing completed with at least one screen reader
- Automated accessibility tests integrated into CI pipeline
Accessibility is an ongoing practice, not a one-time audit. Every new feature, every UI change, and every bug fix should pass through these checks. For more on building polished extension interfaces, see implementing dark mode and extension branding guide.
Continue reading
Related articles
Implementing Dark Mode for Extension UI
Build a polished dark mode for your Chrome extension with CSS custom properties, OS sync, smooth transitions, and accessible contrast ratios. Complete implementation guide.
Extension Options Page UX Best Practices
Design better Chrome extension options pages with proven UX patterns. Covers layout strategies, form design, save behavior, accessibility, and responsive settings interfaces.
Creating a Visual Style Guide for Your Extension
Build a comprehensive visual style guide for your Chrome extension with color palettes, typography scales, spacing systems, and component patterns that ensure consistency.