Documentation Index Fetch the complete documentation index at: https://mintlify.com/microsoft/playwright/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Accessibility testing ensures your web application is usable by people with disabilities. Playwright provides built-in accessibility features and integrations with popular accessibility testing libraries.
Accessibility Snapshot
Playwright provides an accessibility tree snapshot API to inspect the semantic structure of your page.
Basic Snapshot
Snapshot with Root Element
Filter Snapshot
import { test , expect } from '@playwright/test' ;
test ( 'get accessibility snapshot' , async ({ page }) => {
await page . goto ( 'https://example.com' );
const snapshot = await page . accessibility . snapshot ();
console . log ( JSON . stringify ( snapshot , null , 2 ));
});
ARIA Attributes
Test ARIA attributes and roles to ensure proper semantic markup.
Check ARIA Roles
ARIA Labels
ARIA States
import { test , expect } from '@playwright/test' ;
test ( 'verify ARIA roles' , async ({ page }) => {
await page . goto ( 'https://example.com' );
// Check for specific roles
const navigation = page . getByRole ( 'navigation' );
await expect ( navigation ). toBeVisible ();
const main = page . getByRole ( 'main' );
await expect ( main ). toBeVisible ();
const button = page . getByRole ( 'button' , { name: 'Submit' });
await expect ( button ). toBeEnabled ();
});
Keyboard Navigation
Test keyboard accessibility to ensure all functionality is accessible without a mouse.
Tab Navigation
Keyboard Shortcuts
Arrow Key Navigation
import { test , expect } from '@playwright/test' ;
test ( 'keyboard tab navigation' , async ({ page }) => {
await page . goto ( 'https://example.com' );
// Tab through focusable elements
await page . keyboard . press ( 'Tab' );
await expect ( page . locator ( ':focus' )). toHaveAttribute ( 'name' , 'username' );
await page . keyboard . press ( 'Tab' );
await expect ( page . locator ( ':focus' )). toHaveAttribute ( 'name' , 'password' );
await page . keyboard . press ( 'Tab' );
await expect ( page . locator ( ':focus' )). toHaveRole ( 'button' );
});
Focus Management
Test that focus is properly managed in interactive components.
Focus Trap
Focus on Open
Focus on Close
import { test , expect } from '@playwright/test' ;
test ( 'modal focus trap' , async ({ page }) => {
await page . goto ( 'https://example.com' );
await page . getByRole ( 'button' , { name: 'Open modal' }). click ();
const modal = page . getByRole ( 'dialog' );
await expect ( modal ). toBeVisible ();
// Focus should be trapped in modal
const focusableElements = modal . getByRole ( 'button' );
const count = await focusableElements . count ();
// Tab through all elements
for ( let i = 0 ; i < count + 1 ; i ++ ) {
await page . keyboard . press ( 'Tab' );
}
// Focus should cycle back to first element in modal
const focused = page . locator ( ':focus' );
await expect ( focused ). toBeFocused ();
// Verify focus is still in modal
await expect ( modal . locator ( ':focus' )). toBeVisible ();
});
Color Contrast
While Playwright doesn’t have built-in color contrast testing, you can compute contrast ratios.
import { test , expect } from '@playwright/test' ;
function getContrastRatio ( rgb1 : number [], rgb2 : number []) : number {
const getLuminance = ( rgb : number []) => {
const [ r , g , b ] = rgb . map ( val => {
val = val / 255 ;
return val <= 0.03928 ? val / 12.92 : Math . pow (( val + 0.055 ) / 1.055 , 2.4 );
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b ;
};
const lum1 = getLuminance ( rgb1 );
const lum2 = getLuminance ( rgb2 );
const brightest = Math . max ( lum1 , lum2 );
const darkest = Math . min ( lum1 , lum2 );
return ( brightest + 0.05 ) / ( darkest + 0.05 );
}
test ( 'check color contrast' , async ({ page }) => {
await page . goto ( 'https://example.com' );
const button = page . getByRole ( 'button' , { name: 'Submit' });
const colors = await button . evaluate ( el => {
const style = window . getComputedStyle ( el );
const parseRGB = ( color : string ) => {
const match = color . match ( / \d + / g );
return match ? match . map ( Number ) : [ 0 , 0 , 0 ];
};
return {
color: parseRGB ( style . color ),
backgroundColor: parseRGB ( style . backgroundColor ),
};
});
const contrast = getContrastRatio ( colors . color , colors . backgroundColor );
// WCAG AA requires 4.5:1 for normal text
expect ( contrast ). toBeGreaterThanOrEqual ( 4.5 );
});
Screen Reader Testing
Test how your content is announced to screen readers.
import { test , expect } from '@playwright/test' ;
test ( 'verify screen reader text' , async ({ page }) => {
await page . goto ( 'https://example.com' );
// Check for visually hidden but screen-reader accessible text
const srOnly = page . locator ( '.sr-only' );
await expect ( srOnly ). toHaveText ( 'For screen readers only' );
// Verify aria-describedby
const input = page . getByRole ( 'textbox' , { name: 'Email' });
const describedBy = await input . getAttribute ( 'aria-describedby' );
const description = page . locator ( `# ${ describedBy } ` );
await expect ( description ). toHaveText ( 'Enter your email address' );
// Check live regions
const liveRegion = page . locator ( '[aria-live="polite"]' );
await expect ( liveRegion ). toBeEmpty ();
await page . getByRole ( 'button' , { name: 'Load more' }). click ();
await expect ( liveRegion ). toHaveText ( '10 more items loaded' );
});
Ensure forms are accessible with proper labels and error messages.
Form Labels
Error Messages
Required Fields
import { test , expect } from '@playwright/test' ;
test ( 'verify form labels' , async ({ page }) => {
await page . goto ( 'https://example.com/form' );
// Check explicit labels
const emailInput = page . getByRole ( 'textbox' , { name: 'Email address' });
await expect ( emailInput ). toBeVisible ();
// Check implicit labels
const nameInput = page . getByLabel ( 'Full name' );
await expect ( nameInput ). toBeVisible ();
// Verify label association
const passwordInput = page . locator ( '#password' );
const labelFor = await passwordInput . getAttribute ( 'id' );
const label = page . locator ( `label[for=" ${ labelFor } "]` );
await expect ( label ). toHaveText ( 'Password' );
});
Integration with Axe
Integrate with axe-core for comprehensive accessibility testing.
Install Axe
Basic Axe Test
Axe with Specific Rules
Axe Exclude Elements
// npm install --save-dev @axe-core/playwright
Best Practices
Semantic HTML Use semantic HTML elements and ARIA roles to provide meaningful structure.
Keyboard First Test all functionality with keyboard navigation before testing with assistive technologies.
Multiple Methods Combine automated testing (axe) with manual testing using actual screen readers.
Progressive Enhancement Ensure core functionality works without JavaScript before adding enhancements.
Locators Use role-based locators for accessible selectors
Assertions Assert accessibility attributes