LWC Accessibility
Accessibility (Section 508 Compliance)
Use this guidance in order while building components: start with semantic HTML, then add names/descriptions, then verify keyboard and focus behavior, and finally validate screen reader announcements.
Semantic HTML First
Usage: Use native HTML and Lightning base components so assistive technology gets correct semantics by default.
Guidelines:
- Use
<button>for actions and<a>for navigation - Use proper heading hierarchy (
h1,h2,h3, etc.) - Use
<label>for form inputs - Use ARIA roles only when semantic HTML is not sufficient
- Prefer Lightning base components because they include accessible patterns out of the box
Examples:
<!-- Good: Use semantic button -->
<button onclick={handleSubmit} aria-label="Submit form">
Submit
</button>
<!-- Bad: Div masquerading as button -->
<div onclick={handleSubmit} role="button" tabindex="0">
Submit
</div>
<!-- Good: Use semantic link -->
<a href="#" onclick={handleNavigate}>Go to Account</a>
<!-- Good: Proper heading hierarchy -->
<h1>Page Title</h1>
<h2>Section Title</h2>
<h3>Subsection Title</h3>
ARIA Labels and Descriptions
Usage: Always provide ARIA labels for interactive elements and components.
Guidelines:
- Use
aria-labelfor elements without visible text - Use
aria-labelledbyto reference visible labels - Use
aria-describedbyfor additional descriptions - Use
aria-livefor dynamic content updates - Lightning components (like
lightning-input,lightning-button, etc.) automatically generate proper label associations when you provide alabelattribute - noaria-labelneeded in these cases - For
lightning-inputwith hidden labels, uselabel-hiddenattribute along witharia-label
Examples:
<!-- Good: Button with aria-label -->
<button aria-label="Close dialog" onclick={handleClose}>
<lightning-icon icon-name="utility:close"></lightning-icon>
</button>
<!-- Good: lightning-input with label (aria-label not needed - label is automatically linked) -->
<lightning-input
label="Account Name"
value={accountName}
required>
</lightning-input>
<!-- Good: lightning-input without visible label (use label-hidden + aria-label) -->
<lightning-input
label-hidden
aria-label="Account Name"
value={accountName}
required>
</lightning-input>
<!-- Good: Custom input element (aria-label needed - no automatic label) -->
<input
type="text"
aria-label="Account Name"
value={accountName}>
</input>
<!-- Good: Use semantic button element -->
<button
aria-label="Select item"
aria-pressed={isSelected}
onkeydown={handleSelectionKeyDown}
onclick={handleSelect}>
{itemName}
</button>
Keyboard Navigation
Usage: Ensure all interactive elements are keyboard accessible.
Guidelines:
- Use semantic HTML elements (
button,a,input) when possible - Add
tabindex="0"for custom interactive elements - Handle keyboard events with
keydown(avoid relying on deprecatedkeypress) - Support standard keyboard shortcuts (Enter, Space, Escape, Arrow keys)
- Ensure keyboard behavior matches visible behavior and instructions
Examples:
// Good: Handle activation keys
handleActivationKeyDown(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.handleSelect();
} else if (event.key === 'Escape') {
this.handleClose();
}
}
// Good: Handle arrow key navigation
handleListNavigationKeyDown(event) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
this.selectNextItem();
break;
case 'ArrowUp':
event.preventDefault();
this.selectPreviousItem();
break;
case 'Enter':
event.preventDefault();
this.selectCurrentItem();
break;
default:
break;
}
}
Template:
<!-- Good: Keyboard accessible semantic button -->
<button
aria-label="Click to expand"
onkeydown={handleActivationKeyDown}
onclick={handleClick}>
Content
</button>
Focus Management
Usage: Move focus intentionally during UI state changes so keyboard users do not lose context.
Guidelines:
- Set focus on important elements when appropriate
- Return focus to trigger element after modal/dialog closes
- Provide visible focus indicators
- Do not trap focus unless the pattern requires it (for example, active modal dialogs)
Examples:
// Good: Public focus API for parent components
@api focus() {
const input = this.template.querySelector('input');
if (input) {
input.focus();
}
}
// Good: Return focus after action
handleClose() {
const triggerElement = document.activeElement;
this.isOpen = false;
this.returnFocus(triggerElement);
}
Screen Reader Support
Usage: Ensure components expose meaningful names, roles, states, and updates.
Guidelines:
- Provide text alternatives for images and icons
- Use
alttext for images - Use
aria-labeloraria-labelledbyfor icons - Announce dynamic content changes with
aria-live - Use
roleattributes appropriately
Examples:
<!-- Good: Icon with aria-label -->
<lightning-icon
icon-name="utility:info"
alternative-text="Information"
title="Information">
</lightning-icon>
<!-- Good: Dynamic content with aria-live -->
<p aria-live="polite" aria-atomic="true">
{statusMessage}
</p>
<!-- Good: Image with alt text -->
<img src={imageUrl} alt="Account logo" />
Accessibility Best Practices
- Start with semantic HTML and Lightning base components
- Ensure keyboard navigation works for all functionality
- Provide accessible names and descriptions (label,
aria-label,aria-describedby) - Manage focus intentionally during open/close and view transitions
- Test with screen readers and keyboard-only navigation
- Ensure sufficient color contrast (WCAG AA minimum)
Change History
Version 1.0 - 2026-03-26
- Added initial version history tracking for this document.
Last Updated: 2026-03-26 Version: 1.0