How to Test Accessibility Settings on Web (Complete Guide)

Accessibility settings control how users with disabilities perceive and interact with a web application. When these settings are ignored or mis‑implemented, users may encounter unreadable text, inacce

May 31, 2026 · 18 min read · How-To Guides

Introduction: Why Accessibility Settings Testing Matters

Accessibility settings control how users with disabilities perceive and interact with a web application. When these settings are ignored or mis‑implemented, users may encounter unreadable text, inaccessible controls, or broken workflows. In production, a single oversight can lead to lost customers, legal exposure under regulations such as the ADA or EN 301 549, and damage to brand reputation. Testing accessibility settings is therefore not a nicety; it is a risk‑mitigation activity that validates that the UI respects user‑preferred adaptations such as high‑contrast modes, reduced motion, forced colors, and screen‑reader verbosity levels.

Beyond compliance, accessibility testing uncovers usability flaws that affect all users. For example, a button that relies solely on color to convey state fails not only for color‑blind users but also for anyone viewing the screen in bright sunlight. By exercising the full range of browser‑level accessibility preferences, you surface hidden dependencies on visual cues, timing assumptions, and hard‑coded styles that would otherwise remain invisible until a real user encounters them.

The following guide provides a complete, practical framework for testing accessibility settings on web applications. It covers motivation, a detailed test matrix, manual and automated techniques, concrete code examples, persona‑driven autonomous exploration, production‑specific edge cases, a ready‑to‑use checklist, and closing takeaways. Every section is engineered to give you actionable steps you can apply immediately to your codebase.

Understanding Accessibility Settings on the Web

Web browsers expose a set of user‑controlled accessibility preferences that developers can query via CSS media features, JavaScript APIs, or ARIA attributes. These settings influence layout, presentation, and behavior. Knowing which settings exist and how they are signaled is the first step to designing effective tests.

Common Accessibility Settings

SettingCSS Media FeatureJavaScript APITypical User Impact
Prefers‑reduced‑motion@media (prefers-reduced-motion: reduce)window.matchMedia('(prefers-reduced-motion: reduce)')Disables animations, parallax effects, auto‑playing carousels
Prefers‑contrast@media (prefers-contrast: more) / @media (prefers-contrast: less)window.matchMedia('(prefers-contrast: more)')Requests higher or lower contrast; may override author colors
Forced‑colors@media (forced-colors: active)window.matchMedia('(forced-colors: active)')Forces a limited palette (often Windows High Contrast mode)
Prefers‑color‑scheme@media (prefers-color-scheme: dark)window.matchMedia('(prefers-color-scheme: dark)')Switches between light and dark themes
Reduced‑transparency@media (reduced-transparency: reduce)window.matchMedia('(reduced-transparency: reduce)')Requests opaque backgrounds, disables blur effects
Inverted‑colors@media (inverted-colors: inverted)window.matchMedia('(inverted-colors: inverted)')Indicates system‑wide color inversion (e.g., macOS Smart Invert)
Prefers‑reduced‑transparency@media (prefers-reduced-transparency: reduce)window.matchMedia('(prefers-reduced-transparency: reduce)')Similar to reduced‑transparency but less widely supported
Prefers‑reduced‑data@media (prefers-reduced-data: reduce)window.matchMedia('(prefers-reduced-data: reduce)')Signals desire to limit data usage; may affect image resolution
Animation‑durationNot a media feature but readable via getComputedStyle on elements with animation-durationN/AUsers may set global animation speed scales (e.g., Android)

These features are part of the CSS Media Queries Level 5 specification and are implemented in modern browsers (Chrome, Edge, Firefox, Safari). Some settings, such as forced‑colors, are primarily Windows‑specific but still relevant because many assistive technologies rely on the underlying system accessibility layer.

How Browsers Expose Settings

When a user toggles an accessibility option in the operating system or browser settings, the user agent updates the corresponding media feature values. CSS rules that depend on these media features are re‑evaluated instantly, causing a style recalculation and, if necessary, a layout shift. JavaScript can listen for changes via matchMedia.addListener (deprecated) or the newer matchMedia.addEventListener('change', handler).

It is crucial to test both the initial state (as the page loads) and dynamic changes (while the page is already rendered). Many bugs appear only when a setting is toggled after the app has initialized, because JavaScript may have cached values or initialized animations before the media query update.

Test Matrix for Accessibility Settings

A structured test matrix ensures you cover the typical interactions (happy path), failure conditions (error paths), unusual inputs (edge cases), and any security or privacy implications. The matrix below organizes tests by accessibility feature, with columns for each test category.

Accessibility FeatureHappy Path (expected behavior)Error Path (what should not happen)Edge Cases (boundary or rare conditions)Security / Privacy Considerations
Prefers‑reduced‑motionAll non‑essential animations pause or run at reduced speed; essential feedback (e.g., button press) remains.Animations continue at full speed despite user preference.Mixed‑mode animations where some elements ignore the media query due to hard‑coded animation-play-state: running.No direct security impact, but excessive motion can trigger seizures; ensuring reduction protects vulnerable users.
Prefers‑contrast (more)Text and important UI elements meet WCAG AA contrast (≥4.5:1) using the forced high‑contrast palette.Contrast falls below AA; text becomes unreadable.Custom properties that override forced colors via !important or inline styles, causing contrast loss.High‑contrast mode may expose system colors that could be fingerprintable; ensure no leakage of OS theme via canvas reading.
Forced‑colorsAll colors are limited to the system palette; SVG currentColor and CanvasRenderingContext2D respect the forced palette.Elements retain author‑specified colors, breaking the forced‑color contract.Use of filter or mix-blend-mode that attempts to recolor forced‑color elements, leading to unexpected ways.Forced colors can be used to detect assistive technology; avoid exposing forced‑color state to third‑party scripts without consent.
Prefers‑color‑scheme (dark)Dark theme applied; background colors are dark, text light; images with prefers-color-scheme media queries swap appropriately.Light theme persists despite dark preference.Images that ignore picture/source with media attribute, resulting in bright logos on dark background.Dark mode can reduce screen‑burn on OLED; no privacy issue, but ensure no tracking via theme detection.
Reduced‑transparencyBlur, opacity, and semi‑transparent backgrounds become fully opaque; text remains legible.Transparent overlays persist, making text illegible over busy backgrounds.CSS background-blend-mode that attempts to simulate transparency via blend modes.No direct security risk; however, opaque backgrounds may hide underlying content used for CAPTCHA‑like tricks.
Inverted‑colorsColors are inverted system‑wide; content remains readable and interactive elements retain affordance.Inversion causes color‑blindness‑like confusion or makes UI indistinguishable.SVG filters that explicitly set feColorMatrix to counteract inversion, causing double‑inversion artifacts.Color inversion can be used to infer UI state; limit exposure of pixel data via getImageData when inversion is active.
Prefers‑reduced‑dataImages serve low‑resolution variants; non‑essential requests (e.g., analytics, prefetch) are throttled.High‑resolution assets load despite preference; unnecessary network traffic occurs.Service workers that ignore navigator.connection.saveData and cache large assets anyway.Reduced data mode can be used to infer user’s network constraints; avoid fingerprinting via timing differences.

Each row should be validated with both manual checks and automated assertions where possible. The matrix also serves as a backlog for test case creation in your test management system.

Manual Testing Approach

Manual testing remains indispensable for accessibility settings because human perception can detect subtleties that automated tools miss, such as the perceived harshness of a contrast shift or the cognitive load of a motion reduction. The following step‑by‑step procedure outlines a repeatable manual workflow.

Preparation

  1. Identify target settings: Based on your audience, prioritize the most relevant features (e.g., forced‑colors for enterprise users, prefers‑reduced‑motion for vestibular disorder users).
  2. Configure test environments: Use a combination of OS accessibility settings and browser flags. For Windows, enable High Contrast mode via Settings → Ease of Access → High contrast. For macOS, enable Increase Contrast and Reduce Motion under Accessibility. For Linux, use GNOME Tweaks or KDE System Settings.
  3. Prepare a test charter: Write a short scenario for each feature (e.g., “Navigate the checkout flow with forced‑colors active”). Include expected outcomes and acceptance criteria.
  4. Gather tools:

Step‑by‑Step Procedure

  1. Baseline capture: With default OS/browser settings, record a short video or screenshot set of key pages (home, login, product detail, checkout). This serves as a reference for visual comparison.
  2. Apply first setting: Enable the target accessibility feature at the OS level (or via DevTools emulation). Wait for the page to recompute styles (usually <200 ms).
  3. Perform happy‑path tasks: Complete the core user flow defined in your charter using only keyboard or screen reader as appropriate. Observe:
  1. Introduce error conditions: Deliberately violate a best practice (e.g., add a low‑contrast button via inline style) and verify that the setting still forces an accessible outcome or at least does not worsen the problem.
  2. Explore edge cases:
  1. Document findings: For each observation, note whether the behavior passed, failed, or produced a warning. Capture screenshots or video clips for failed cases.
  2. Reset and repeat: Disable the setting, return to baseline, and proceed to the next feature.

Tools for Manual Testing

Automated Approaches

Automated testing scales validation across CI pipelines and regression suites. While no tool can fully replace human judgment, combining static analysis, unit tests, and end‑to‑end (E2E) frameworks yields high coverage.

Unit/Integration Tests with Jest + axe-core

For component libraries (React, Vue, Svelte), you can render components in a JSDOM environment and run axe-core to inspect accessibility violations under simulated media features.


// jest.setup.js
import { configure } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

// Example test for prefers-reduced-motion
test('Button respects prefers-reduced-motion', async () => {
  // Simulate reduced motion media query
  Object.defineProperty(window, 'matchMedia', {
    writable: true,
    value: jest.fn().mockImplementation(query => ({
      matches: query.includes('prefers-reduced-motion'),
      media: query,
      onchange: null,
      addListener: jest.fn(), // deprecated
      removeListener: jest.fn(),
      addEventListener: (_, cb) => {
        this._listener = cb;
      },
      removeEventListener: (_, cb) => {
        this._listener = null;
      },
      dispatchEvent: (event) => {
        if (this._listener) this._listener(event);
      }
    }))
  });

  const { container } = render(<PrimaryButton label="Save" />);
  // Trigger a change event to ensure components react
  window.matchMedia('(prefers-reduced-motion: reduce)').dispatchEvent(new Event('change'));

  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Key points:

End‑to‑End Tests with Cypress + axe

Cypress interacts with the real browser, allowing you to toggle OS‑level settings via Chrome DevTools Protocol or external utilities.


// cypress/integration/accessibility_settings.spec.js
describe('Forced colors mode', () => {
  beforeEach(() => {
    // Enable forced colors via DevTools protocol
    cy.visit('/login');
    cy.then(() => {
      return cy.automation('send', 'Emulation.setEmulatedMedia', {
        features: [
          { name: 'forced-colors', value: 'active' }
        ]
      });
    });
  });

  it('should maintain contrast and operable controls', () => {
    cy.injectAxe(); // custom command that loads axe core
    cy.checkA11y(null, {
      // exclude known false positives if any
      exclude: [".cookie-banner"]
    });
  });

  it('should respect user‑initiated toggle', () => {
    // Simulate user turning off forced colors via OS (not possible in CI)
    // Instead we toggle via DevTools again
    cy.automation('send', 'Emulation.setEmulatedMedia', {
      features: [{ name: 'forced-colors', value: 'none' }]
    });
    cy.checkA11y();
  });
});

The cy.automation command is a wrapper around Chrome DevTools Protocol that you can add via cypress-real-events or a custom plugin.

Using Lighthouse CI

Lighthouse includes an accessibility audit that runs a set of axe‑based checks. You can configure Lighthouse CI to test multiple emulator states.


// lighthouserc.json
{
  "ci": {
    "collect": {
      "url": ["http://localhost:3000"],
      "settings": {
        "emulatedFormFactor": "desktop",
        "screenEmulation": {
          "disabled": false,
          "width": 1280,
          "height": 800,
          "deviceScaleFactor": 1,
          "mobile": false
        }
      }
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:accessibility": ["warn", { "minScore": 0.9 }]
      }
    }
  }
}

To test forced colors, launch Chrome with the --force-color-profile flag or use the DevTools protocol within a custom Lighthouse plugin.

Playwright with axe

Playwright offers built‑in support for emulating media features and integrates neatly with the @playwright/test runner.


// tests/accessibility.spec.js
const { test, expect } = require('@playwright/test');
const { injectAxe, checkA11y } = require('playwright-axe');

test.describe('Prefers-reduced-motion', () => {
  test.use({ 
    // Emulate reduced motion at the browser context level
    reducedMotion: 'reduce'
  });

  test('animations are disabled', async ({ page }) => {
    await page.goto('/dashboard');
    await injectAxe(page);
    const { violations } = await checkA11y(page, {
      // axe rule that detects animation violations
      runOnly: { type: 'tag', values: ['animation'] }
    });
    expect(violations).toEqual([]);
  });
});

Playwright’s use({ reducedMotion: 'reduce' }) automatically sets the appropriate CSS media feature, eliminating the need to mock matchMedia.

Code Examples

Concrete snippets illustrate how to assert specific accessibility‑setting behaviors in automated tests.

Example: Testing Color Contrast via axe

This example checks that all text meets AA contrast when forced‑colors is active.


// test/contrast.forced-colors.test.js
const { test, expect } = require('@playwright/test');
const { injectAxe, checkA11y } = require('playwright-axe');

test.use({ forcedColors: 'active' });

test('forced colors yields sufficient contrast', async ({ page }) => {
  await page.goto('/product/123');
  await injectAxe(page);
  const { violations } = await checkA11y(page, {
    // limit to contrast-related rules
    runOnly: { type: 'tag', values: ['color'] }
  });
  expect(violations).toHaveLength(0);
});

If any element fails contrast, violations will contain objects with id: 'color-contrast' and detailed failure messages.

Example: Keyboard Navigation Test

Ensures that a modal can be opened, interacted with, and closed using only the Tab key when prefers‑reduced‑motion is enabled (to confirm that no motion‑dependent focus traps exist).


// test/keyboard.modal.test.js
const { test, expect } = require('@playwright/test');

test.use({ reducedMotion: 'reduce' });

test('modal is keyboard operable under reduced motion', async ({ page }) => {
  await page.goto('/settings');
  // Open modal via button
  await page.click('button#open-preferences');
  // Wait for modal to be visible
  await page.waitForSelector('div[role="dialog"]', { state: 'visible' });

  // Trap focus inside modal: pressing Tab should cycle through focusable elements
  const focusable = await page.$$eval('div[role="dialog"] button, div[role="dialog"] input, div[role="dialog"] select', els => els.map(e => e.tabIndex));
  expect(focusable.every(i => i >= 0)).toBeTruthy();

  // Close modal via Escape key
  await page.keyboard.press('Escape');
  await expect(page.locator('div[role="dialog"]')).toBeHidden();
});

Example: Screen Reader Announcements with Playwright

Verifies that a live region announces a status change when reduced‑motion is on, ensuring that the announcement is not delayed by an animation.


// test/sr.announcement.test.js
const { test, expect } = require('@playwright/test');

test.use({ reducedMotion: 'reduce' });

test('status message is announced promptly', async ({ page }) => {
  await page.goto('/checkout');
  // Listen for aria-live changes
  const messages = [];
  page.on('console', msg => {
    if (msg.type() === 'log' && msg.text().startsWith('SR:')) {
      messages.push(msg.text().substring(4));
    }
  });

  // Expose a helper that logs live region updates
  await page.addInitScript(() => {
    const observer = new MutationObserver((mutations) => {
      for (const mut of mutations) {
        if (mut.type === 'characterData' && mut.target.parentElement.getAttribute('aria-live')) {
          console.log('SR:' + mut.target.textContent);
        }
      }
    });
    document.querySelectorAll('[aria-live]').forEach(el => observer.observe(el, { characterData: true, subtree: true }));
  });

  // Trigger an update that would normally be accompanied by a spinner
  await page.click('button#apply-coupon');
  await page.waitForTimeout(300); // give time for live region update

  expect(messages).toContain('Coupon applied successfully');
});

This test uses a mutation observer to pipe live‑region updates to the console, where the test harness captures them. It validates that the announcement appears promptly, not after a lengthy animation that would violate the user’s reduced‑motion preference.

Autonomous, Persona‑Driven Exploration

Scripted tests excel at checking known conditions, but they cannot anticipate every combination of user behavior, device state, and environmental nuance. Autonomous testing platforms that simulate diverse user personas can surface accessibility‑settings bugs that remain hidden in deterministic test suites.

How Persona Models Work

A persona model encodes a set of behavioral tendencies:

Each persona drives a stochastic exploration engine that generates input sequences (taps, scrolls, keystrokes, voice commands) while monitoring the application for crashes, ANRs, dead ends, and accessibility violations. The engine maintains a visited‑state graph to avoid redundant exploration and learns which paths lead to dead ends, thereby increasing coverage over successive runs.

What They Find That Scripts Miss

  1. Conditional media‑query overrides: A component may check window.matchMedia('(prefers-reduced-motion: reduce)') only on mount and never re‑evaluate. A script that sets the media query before rendering will pass, but a persona that toggles the setting after interacting with a dynamic widget (e.g., a chart that redraws on zoom) will catch the missed update.
  2. Focus‑traps revealed by impatient navigation: An impatient persona may repeatedly tab through a modal before its entrance animation finishes, exposing a scenario where focus escapes to the background because the trap was enabled only after a transitionend event.
  3. Contrast regression caused by theme‑switching races: A curious persona that toggles dark mode, then immediately enables forced‑colors, can trigger a race where the CSS custom properties have not yet been updated, resulting in a brief flash of illegible text.
  4. Screen‑reader verbosity mismatches: An accessibility persona using a screen reader with high verbosity may hear excessive duplicate announcements when a live region is updated multiple times in quick succession due to a debounce bug that only manifests under rapid interaction patterns.
  5. Dead buttons exposed by adversarial input: An adversarial persona that fills a form with extremely long strings can cause overflow that pushes a call‑to‑action button outside the viewport, making it unreachable via touch or keyboard—something a script with fixed‑length test data would never see.

Integrating SUSA (mention here)

SUSA’s autonomous agent can be pointed at a staging URL or fed an APK‑derived web wrapper. By configuring a persona profile that includes “accessibility” and “elderly” traits, the agent will automatically enable system‑level accessibility settings (forced‑colors, high contrast, reduced‑motion) and explore the application for extended periods. The resulting report highlights:

Because the agent builds a session‑level memory of visited screens, each subsequent run becomes smarter, revisiting previously problematic areas with varied input patterns to confirm fixes. This continuous feedback loop complements traditional unit and E2E tests, catching regressions that arise from complex interaction‑state combinations rather than isolated code paths.

Production Edge Cases

Even with thorough lab testing, certain conditions only surface in production due to real‑world variability, third‑party integrations, or deployment‑specific quirks. Anticipating these helps you design mitigations and monitoring.

Dynamic Theme Switching

Many applications allow users to switch themes via a UI toggle that persists via localStorage. If the theme switch does not force a recomputation of media‑query‑dependent styles, a user who has forced‑colors enabled may see a mismatch: the theme’s custom properties override the forced palette, causing contrast loss.

Mitigation:

User‑Generated Content

Comments, reviews, or markdown‑authored posts can introduce inline styles or arbitrary HTML that bypasses your CSS architecture. A user could paste a block that remains visible under forced‑colors, creating a low‑contrast island.

Mitigation:

Third‑Party Widgets

Embedded calendars, payment iframes, or social‑share buttons often come with their own CSS that may not respect forced‑colors or reduced‑motion. Because they load from external domains, your page’s media‑query listeners cannot affect them directly.

Mitigation:

Server‑Side Rendering Hydration Mismatch

When SSR generates HTML assuming a default theme (e.g., light) but the client has forced‑colors active, the initial paint may show incorrect colors before JavaScript rehydrates and applies the proper styles. This flash can be disorienting for low‑vision users.

Mitigation:

Network‑Throttling Interaction

A user with prefers‑reduced‑data enabled may experience delayed image lazy‑loading, causing layout shifts that affect reading order. If your lazy‑loader relies on IntersectionObserver thresholds that assume immediate image availability, the placeholder may remain too long, pushing content down and breaking the tab order.

Mitigation:

By incorporating these edge‑case considerations into your test matrix and runtime guards, you reduce the likelihood that a setting‑related bug escapes to production.

Checklist for Accessibility Settings Testing

Use this checklist before each release or as part of your Definition of Done.

✅ ItemDescriptionHow to Verify
1All non‑essential animations respect prefers-reduced-motion: reduceManual toggle + DevTools animation panel; automated axe rule animation
2Text and UI components meet WCAG AA contrast under forced-colors: activeManual high‑contrast mode; automated contrast audit
3Color scheme switches correctly with prefers-color-schemeManual dark/light toggle; automated media‑query test
4Layout remains usable when reduced-transparency: reduce is activeManual enable reduced transparency; check for opaque backgrounds
5Screen reader announces dynamic changes promptly under accessibility personaManual NVDA/VoiceOver test; automated live‑region check via Playwright
6Keyboard focus never leaves modal/dialog when any accessibility setting is onManual tab navigation; automated focus‑trap test
7No horizontal scrolling or content loss at 200% zoom combined with any settingManual zoom + setting toggle; automated viewport check
8Third‑party iframes do not introduce contrast or focus violationsManual inspect iframe; automated axe on iframe content
9CSP and sanitization block inline styles that could override forced colorsManual attempt to inject style; automated security scan
10SSR hydration does not produce a flash of incorrect colors under forced colorsNetwork throttled load + visual diff; automated screenshot diff on hydrate
11Data‑saving mode does not break lazy‑loaded image placeholdersManual SaveData toggle; automated IntersectionObserver latency test
12Persona‑driven autonomous exploration (e.g., SUSA) reports zero new accessibility defectsRun agent with accessibility + elderly personas; review report

Mark each item as PASS, FAIL, or NA (not applicable). Treat any FAIL as a release blocker unless a justified risk acceptance is documented.

Closing Takeaways

Testing accessibility settings is a disciplined, repeatable activity that protects users, satisfies legal obligations, and improves overall product quality. Begin by enumerating the settings your audience is likely to use, then construct a test matrix that maps each setting to happy‑path, error‑path, edge‑case, and security considerations.

Manual testing remains the gold standard for validating subjective experiences such as perceived contrast or motion sickness; pair it with DevTools emulation and OS‑level toggles for fast iteration. Automate the repeatable portions using axe‑based assertions in unit, integration, and E2E tests, leveraging tools like Playwright, Cypress, and Lighthouse CI to simulate media‑feature states in CI pipelines.

Go beyond scripted checks by employing autonomous, persona‑driven exploration. Platforms such as

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free