Build accessibility into the definition of done
A reliable workflow mixes three layers. First, developers use semantic elements and accessible component APIs while building. Next, automated checks catch obvious regressions in pull requests. Finally, a person walks through the important user journeys with a keyboard and a screen reader. Each layer catches a different class of problem, so none is a substitute for the others.
Component contract
Document required labels, error text, focus behavior, and keyboard controls for reusable UI.
Journey review
Test sign-in, search, checkout, and other high-value flows as a user would navigate them.
Regression signal
Run automated checks consistently, then triage findings by user impact instead of chasing a score.
Keep interactive browser code in focused client components, but keep authorization and data decisions on the server. Our React Server Components boundary guide explains how to keep that division clear.
Start with semantic HTML before adding ARIA
Native HTML carries behavior that custom divs do not: buttons respond to keyboard input, inputs expose values and errors, and headings describe page structure. Prefer a realbutton for an action, an a for navigation, and a label connected to every form control. Add ARIA only when native HTML cannot express the needed relationship or state.
function SaveProfileButton({ pending, onSave }) {
return (
<button type="button" disabled={pending} onClick={onSave}>
{pending ? "Saving profile…" : "Save profile"}
</button>
);
}
function EmailField({ error }) {
return (
<div>
<label htmlFor="email">Work email</label>
<input id="email" name="email" type="email" aria-describedby={error ? "email-error" : undefined} />
{error && <p id="email-error">{error}</p>}
</div>
);
}Test the keyboard journey and focus lifecycle
Put the mouse away and use Tab, Shift+Tab, Enter, Space, and Escape. The focus indicator must be visible, the sequence must follow the visual task order, and every opened surface must have a predictable way out. When a modal opens, move focus into it; when it closes, return focus to the trigger unless the workflow has moved elsewhere.
Avoid positive tabIndex values; they create a separate, fragile tab order. If a route changes after a form submission, give the new page a clear heading and ensure errors or success feedback are discoverable without requiring the user to hunt for them.
Make validation and async states understandable
Form validation should identify what failed, explain how to fix it, and preserve the user’s entered data. Do not rely on color alone. For asynchronous work, disable only the action that cannot safely repeat, retain context, and expose a concise status update. Error boundaries should also provide a recovery path instead of a blank region; see our React error-boundary recovery guide for that pattern.
- Use a visible label for every input; placeholders are hints, not labels.
- Connect field-level errors with
aria-describedbywhen relevant. - Move focus to an error summary only when it helps the user recover.
- Use plain, specific language: “Enter a valid work email,” not “Invalid input.”
- Announce meaningful saved, failed, or loading outcomes without interrupting typing.
Automate repeatable checks, then test real behavior
Lint rules and browser accessibility scanners are valuable guardrails. Run them against shared components and key routes, and fix clear issues such as missing names, invalid landmark nesting, or weak color contrast. They cannot decide whether a label is useful, whether focus is surprising, or whether an announcement arrives at the right moment.
// Example acceptance test outline
// 1. Open the page with a keyboard-only test.
// 2. Tab through every interactive control in task order.
// 3. Open and close each modal, menu, and error state.
// 4. Confirm a visible focus indicator and useful accessible name.
// 5. Repeat the highest-value task with a screen reader.Treat accessibility regressions like performance or security regressions: make them easy to observe, assign an owner, and prevent recurrence with a focused test. Secret values and permission decisions should remain server-side throughout these flows; the Next.js secret-management guide covers that boundary.
React accessibility release checklist
✓ Page has one clear h1 and a logical heading order
✓ Landmarks and controls use native semantic elements
✓ Every interactive element has an accessible name
✓ Keyboard focus is visible, ordered, and never trapped unintentionally
✓ Dialogs, menus, and popovers manage focus and Escape consistently
✓ Forms expose labels, errors, required state, and async feedback clearly
✓ Color is not the only way information is conveyed
✓ Critical journeys receive keyboard and screen-reader review before release
Build React products that more people can use
Endurance Softwares helps teams improve React and Next.js products with practical quality, accessibility, performance, and architecture reviews.