Build inclusive web interfaces by understanding semantic HTML, keyboard interaction, focus, accessible names, forms, color contrast, images, motion, ARIA, and assistive technology.
Explain why accessibility is a core part of interface quality
Use semantic HTML to expose meaningful structure and controls
Create interfaces that work with keyboards and assistive technology
Provide accessible names, labels, descriptions, and error messages
Design sufficient color contrast and non-color status indicators
Apply ARIA only when native HTML cannot express the required semantics
Accessible interfaces allow people with different abilities, devices, and interaction methods to perceive, understand, navigate, and operate a product.
Accessibility is not a separate visual mode. It should be considered throughout structure, interaction design, content, implementation, and testing.
Accessibility benefits many different users and situations
Design for multiple ways of perceiving and operating interfaces
Build accessibility into components from the beginning
A usable action should not depend only on:
- Seeing a particular color
- Using a mouse
- Hearing an audio notification
- Performing a precise gesture
Provide equivalent information and interaction
through appropriate alternatives.Native HTML elements communicate roles, relationships, and behavior to browsers and assistive technologies.
Using the correct semantic element often provides keyboard behavior, focus handling, and accessibility information without additional JavaScript.
Choose elements based on meaning
Use buttons for actions and links for navigation
Prefer native controls before creating custom widgets
<!-- Preferred -->
<button type="button">
Open settings
</button>
<!-- Avoid recreating a button with a div -->
<div class="button">
Open settings
</div>Landmarks allow assistive technology users to move quickly between major page regions such as navigation and main content.
Headings create a logical content hierarchy and help users understand relationships between sections.
Use semantic page landmarks
Maintain a logical heading hierarchy
Do not choose heading levels based only on visual size
<header>
<nav aria-label="Primary">
<a href="/courses">Courses</a>
</nav>
</header>
<main>
<h1>Account settings</h1>
<section>
<h2>Profile</h2>
</section>
<section>
<h2>Security</h2>
</section>
</main>Interactive functionality should be usable without requiring a mouse. Native controls already provide expected keyboard behavior.
Keyboard users need a logical tab order and must not become trapped inside custom components.
Ensure interactive controls are keyboard operable
Preserve logical focus order
Avoid positive tabindex values
<form>
<label for="email">Email</label>
<input id="email" type="email" />
<label for="message">Message</label>
<textarea id="message"></textarea>
<button type="submit">
Send message
</button>
</form>Keyboard focus tells users which interactive element will receive the next keyboard action. Focus should always remain visible.
Dynamic interfaces may need deliberate focus management when opening dialogs, moving between views, or restoring context after an interaction.
Never remove focus indication without providing a replacement
Use focus-visible for intentional keyboard focus styles
Move focus only when doing so helps preserve user context
.button:focus-visible {
outline: 3px solid #60a5fa;
outline-offset: 3px;
}
.input:focus-visible {
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}Interactive controls need an accessible name that communicates their purpose to assistive technologies.
Visible text is usually the preferred naming mechanism. aria-label and aria-labelledby are useful when visible text cannot provide the required name.
Every interactive control needs a meaningful name
Prefer visible labels when possible
Provide names for icon-only controls
<button
type="button"
aria-label="Delete course"
>
<svg
aria-hidden="true"
viewBox="0 0 24 24"
>
<path d="M6 7h12M9 7V5h6v2" />
</svg>
</button>Form controls should have labels that explain what information is expected. Supporting text and validation errors should be programmatically associated with the relevant control.
Placeholder text should not replace a persistent label because it disappears as users enter information.
Associate labels with controls
Connect supporting and error text programmatically
Do not use placeholders as the only label
<label for="email">
Email address
</label>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<p id="email-error" role="alert">
Enter a valid email address.
</p>Text and interactive elements need enough contrast against their backgrounds to remain distinguishable for users with low vision or color-vision differences.
Color should not be the only signal used to communicate state, errors, success, or selection.
Maintain sufficient foreground-background contrast
Do not communicate meaning through color alone
Pair color with text, icons, patterns, or other indicators
export function Status() {
return (
<span className="status status--error">
<span aria-hidden="true">●</span>
Payment failed
</span>
);
}
// The text communicates the state even when
// the color itself cannot be distinguished.Meaningful images need text alternatives that communicate their purpose or essential information.
Decorative images should generally use an empty alt attribute so they do not add unnecessary noise for screen reader users.
Describe meaningful images according to their purpose
Use empty alt text for purely decorative images
Do not repeat surrounding text unnecessarily
<img
src="progress-chart.png"
alt="Course completion increased from 45% to 72%"
/>
<img
src="decorative-gradient.svg"
alt=""
/>Animation can clarify relationships and state changes, but unnecessary movement can create discomfort or interfere with comprehension for some users.
Interfaces should respect reduced-motion preferences and avoid making essential information depend entirely on animation.
Use motion to support understanding
Respect prefers-reduced-motion
Avoid unnecessary flashing and continuous movement
.panel {
transition: transform 220ms ease;
}
.panel[data-open="true"] {
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.panel {
transition: none;
}
}ARIA adds semantic information when native HTML cannot represent a required widget or relationship.
ARIA does not automatically provide keyboard interaction or behavior. Adding a role to a generic element does not make it equivalent to a native control.
Prefer native HTML whenever possible
Use ARIA to add missing semantics rather than replace existing semantics
Implement required keyboard behavior for custom widgets
function Disclosure({ open, onToggle }) {
return (
<>
<button
type="button"
aria-expanded={open}
aria-controls="lesson-details"
onClick={onToggle}
>
Lesson details
</button>
<div
id="lesson-details"
hidden={!open}
>
Additional lesson information
</div>
</>
);
}Screen readers expose semantic information from the accessibility tree and allow users to navigate by controls, headings, landmarks, and other structures.
Dynamic updates that do not receive focus may require a live region when users need to be informed that something changed.
Use semantic structure to produce a meaningful accessibility tree
Use live regions selectively for important dynamic updates
Do not announce every visual change
export function SaveStatus({ saved }: { saved: boolean }) {
return (
<p aria-live="polite">
{saved
? "Changes saved successfully."
: "You have unsaved changes."}
</p>
);
}