# Accessibility Guide for LWC Comprehensive guide to building WCAG 2.1 AA compliant Lightning Web Components. --- ## Table of Contents 1. [Accessibility Standards](#accessibility-standards) 2. [Semantic HTML](#semantic-html) 3. [ARIA Attributes](#aria-attributes) 4. [Keyboard Navigation](#keyboard-navigation) 5. [Focus Management](#focus-management) 6. [Color and Contrast](#color-and-contrast) 7. [Screen Reader Support](#screen-reader-support) 8. [Live Regions](#live-regions) 9. [Form Accessibility](#form-accessibility) 10. [Common Patterns](#common-patterns) 11. [Testing](#testing) 12. [Tools and Resources](#tools-and-resources) --- ## Accessibility Standards ### WCAG 2.1 AA Compliance All Lightning Web Components should meet WCAG 2.1 Level AA standards. | Principle | Description | |-----------|-------------| | **Perceivable** | Information must be presentable to users in ways they can perceive | | **Operable** | UI components must be operable (keyboard, mouse, voice) | | **Understandable** | Information and UI must be understandable | | **Robust** | Content must work with assistive technologies | ### Key Requirements | Requirement | Standard | Implementation | |-------------|----------|----------------| | **Color contrast** | 4.5:1 for normal text, 3:1 for large text | Use SLDS color tokens | | **Keyboard navigation** | All interactive elements accessible via keyboard | Tab order, Enter/Space triggers | | **Screen reader support** | ARIA labels, roles, live regions | Proper semantic HTML + ARIA | | **Focus indicators** | Visible focus state | Use SLDS focus utilities | | **Alternative text** | All images have alt text | `alt` attribute on images | --- ## Semantic HTML ### Use Proper HTML Elements ```html
Click me
``` ### Headings Hierarchy ```html

Page Title

Subsection

Page Title

Main Section

Subsection

``` ### Landmarks ```html ``` --- ## ARIA Attributes ### ARIA Labels ```html
Enter phone number with country code
``` ### ARIA Roles ```html
Item 1
Item 2
``` ### ARIA States ```html
``` --- ## Keyboard Navigation ### Tab Order ```html
Skip to main content
``` ### Keyboard Event Handlers ```javascript // accountCard.js export default class AccountCard extends LightningElement { handleKeyDown(event) { // Enter or Space activates if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.handleSelect(); } // Arrow navigation if (event.key === 'ArrowDown') { event.preventDefault(); this.focusNextItem(); } else if (event.key === 'ArrowUp') { event.preventDefault(); this.focusPreviousItem(); } // Escape closes if (event.key === 'Escape') { this.handleClose(); } } focusNextItem() { const items = this.template.querySelectorAll('[role="listitem"]'); const currentIndex = Array.from(items).indexOf(document.activeElement); const nextIndex = (currentIndex + 1) % items.length; items[nextIndex].focus(); } focusPreviousItem() { const items = this.template.querySelectorAll('[role="listitem"]'); const currentIndex = Array.from(items).indexOf(document.activeElement); const prevIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1; items[prevIndex].focus(); } } ``` ```html ``` --- ## Focus Management ### Focus Trap in Modals ```javascript // composableModal.js export default class ComposableModal extends LightningElement { _focusableElements = []; _isOpen = false; @api toggleModal() { this._isOpen = !this._isOpen; if (this._isOpen) { // Capture focusable elements this._focusableElements = this.getFocusableElements(); // Focus first element requestAnimationFrame(() => { this._focusableElements[0]?.focus(); }); // Add keyboard listener window.addEventListener('keydown', this._handleKeyDown); // Store previous focus this._previousFocus = document.activeElement; } else { // Remove keyboard listener window.removeEventListener('keydown', this._handleKeyDown); // Restore focus this._previousFocus?.focus(); } } getFocusableElements() { const selector = [ 'a[href]', 'button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[tabindex]:not([tabindex="-1"])' ].join(','); return Array.from(this.template.querySelectorAll(selector)); } _handleKeyDown = (event) => { if (event.key === 'Tab') { this.trapFocus(event); } else if (event.key === 'Escape') { this.toggleModal(); } } trapFocus(event) { const firstElement = this._focusableElements[0]; const lastElement = this._focusableElements[this._focusableElements.length - 1]; const activeElement = this.template.activeElement; if (event.shiftKey) { // Shift+Tab: Moving backward if (activeElement === firstElement) { event.preventDefault(); lastElement.focus(); } } else { // Tab: Moving forward if (activeElement === lastElement) { event.preventDefault(); firstElement.focus(); } } } disconnectedCallback() { window.removeEventListener('keydown', this._handleKeyDown); } } ``` ### Managing Focus After Actions ```javascript handleDelete(event) { const itemId = event.target.dataset.id; const itemIndex = this.items.findIndex(item => item.Id === itemId); // Delete item this.items = this.items.filter(item => item.Id !== itemId); // Focus next item or previous if last item deleted requestAnimationFrame(() => { const focusIndex = itemIndex < this.items.length ? itemIndex : itemIndex - 1; if (focusIndex >= 0) { const nextItem = this.template.querySelector(`[data-id="${this.items[focusIndex].Id}"]`); nextItem?.focus(); } }); } ``` --- ## Color and Contrast ### Use SLDS Color Tokens ```css /* BAD: Hardcoded colors */ .my-component { color: #333333; background-color: #ffffff; border-color: #dddddd; } /* GOOD: SLDS tokens with proper contrast */ .my-component { color: var(--slds-g-color-on-surface, #181818); background-color: var(--slds-g-color-surface-container-1, #ffffff); border-color: var(--slds-g-color-border-1, #c9c9c9); } ``` ### Testing Contrast ```html

Regular body text

Large heading

Visit our help center for support.

``` ### Color Independence ```html Error Error
Failed
``` --- ## Screen Reader Support ### Assistive Text ```html Required field ``` ### Image Alternative Text ```html Sales trend showing 15% increase over last quarter View your profile ``` --- ## Live Regions ### ARIA Live Regions ```javascript // notificationComponent.js export default class NotificationComponent extends LightningElement { @track messages = []; addMessage(message, type = 'info') { const id = Date.now(); this.messages = [...this.messages, { id, message, type }]; // Auto-remove after 5 seconds setTimeout(() => { this.messages = this.messages.filter(m => m.id !== id); }, 5000); } } ``` ```html ``` ### Assertive vs Polite ```html
{searchResultsCount} results found
``` --- ## Form Accessibility ### Accessible Form Fields ```html ``` ### Fieldset and Legend ```html
Contact Method *
``` --- ## Common Patterns ### Accessible Tabs ```javascript // tabsComponent.js export default class TabsComponent extends LightningElement { @track activeTab = 'tab1'; handleTabKeyDown(event) { const tabs = Array.from(this.template.querySelectorAll('[role="tab"]')); const currentIndex = tabs.indexOf(event.target); let nextIndex; if (event.key === 'ArrowRight') { nextIndex = (currentIndex + 1) % tabs.length; } else if (event.key === 'ArrowLeft') { nextIndex = currentIndex === 0 ? tabs.length - 1 : currentIndex - 1; } else if (event.key === 'Home') { nextIndex = 0; } else if (event.key === 'End') { nextIndex = tabs.length - 1; } if (nextIndex !== undefined) { event.preventDefault(); tabs[nextIndex].focus(); this.activeTab = tabs[nextIndex].dataset.tab; } } handleTabClick(event) { this.activeTab = event.currentTarget.dataset.tab; } } ``` ```html ``` ### Accessible Data Table ```html ``` --- ## Testing ### Automated Testing ```javascript // Jest accessibility tests it('has proper ARIA labels', () => { const element = createElement('c-my-component', { is: MyComponent }); document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); expect(button.getAttribute('aria-label')).toBeTruthy(); }); it('manages focus when modal opens', async () => { const element = createElement('c-modal', { is: Modal }); document.body.appendChild(element); element.openModal(); await flushPromises(); const firstFocusable = element.shadowRoot.querySelector('.focusable'); expect(document.activeElement).toBe(firstFocusable); }); it('announces status changes to screen readers', async () => { const element = createElement('c-notification', { is: Notification }); document.body.appendChild(element); element.showMessage('Success'); await flushPromises(); const liveRegion = element.shadowRoot.querySelector('[aria-live]'); expect(liveRegion.textContent).toContain('Success'); }); ``` ### Manual Testing Checklist - [ ] Navigate entire component using only keyboard (Tab, Shift+Tab, Enter, Space, Arrows) - [ ] Test with screen reader (NVDA, JAWS, VoiceOver) - [ ] Verify color contrast ratios (4.5:1 minimum for text) - [ ] Test at 200% zoom - [ ] Verify focus indicators are visible - [ ] Test with high contrast mode - [ ] Verify all interactive elements have accessible names - [ ] Test form validation announcements --- ## Tools and Resources ### Browser Extensions | Tool | Purpose | |------|---------| | **axe DevTools** | Automated accessibility testing | | **Lighthouse** | Built into Chrome DevTools, accessibility audit | | **WAVE** | Visual accessibility evaluation | | **Color Contrast Analyzer** | Check WCAG contrast compliance | ### Screen Readers | Platform | Screen Reader | |----------|---------------| | Windows | NVDA (free), JAWS | | macOS | VoiceOver (built-in) | | iOS | VoiceOver (built-in) | | Android | TalkBack (built-in) | ### Testing Commands ```bash # Run axe accessibility tests npm install --save-dev @axe-core/cli axe https://your-app.lightning.force.com # Lighthouse CLI npm install -g lighthouse lighthouse https://your-app.lightning.force.com --only-categories=accessibility ``` ### Resources - [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) - [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/) - [Salesforce Lightning Design System Accessibility](https://www.lightningdesignsystem.com/accessibility/overview/) - [WebAIM Resources](https://webaim.org/resources/) --- ## Related Resources - [component-patterns.md](component-patterns.md) - Implementation patterns - [jest-testing.md](jest-testing.md) - Testing strategies - [performance-guide.md](performance-guide.md) - Performance optimization