Semantic HTML, ARIA, and keyboard navigation on a real e-commerce rebuild — what we implemented, how we tested it, and why accessibility also improved mobile conversion.
On the medical storefront, accessibility work sat next to responsive layouts. Touch-friendly components and WCAG 2.1 basics shipped together. Mobile conversion rose about 22%. Some of that was layout. Some of that was the same discipline: larger targets, clearer labels, fewer traps.
WCAG 2.1 AA was the bar: perceivable, operable, understandable, robust. We did not chase AAA on marketing flourishes. We did not ship a checkout a keyboard user could not finish.
ARIA is a patch when HTML is not enough. Most product UI never needs role="button" if you used <button>.
// Wrong: clickable div, no keyboard, no name
<div className="card" onClick={addToCart}>Add to cart</div>
// Right
<button type="button" onClick={addToCart}>
Add to cart
</button>
Landmarks we required on every template:
header / nav with an accessible namemainfooter#mainHeadings must nest. A product name as h1, variants as h2, not a soup of styled p tags.
<a href="#main" className="skip-link">
Skip to content
</a>
<main id="main">...</main>
If you can click it, you can Tab to it, activate it with Enter/Space, and see focus.
.btn:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
Do not outline: none without a replacement. Modals must:
useEffect(() => {
if (!open) return;
const previouslyFocused = document.activeElement as HTMLElement | null;
closeRef.current?.focus();
return () => previouslyFocused?.focus();
}, [open]);
Mega-menus and custom selects are where keyboard support usually dies. Prefer native <select> until the design truly needs a listbox.
Checkout is an a11y exam.
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
autoComplete="email"
aria-invalid={Boolean(error)}
aria-describedby={error ? 'email-error' : undefined}
/>
{error ? (
<p id="email-error" role="alert">
{error}
</p>
) : null}
role="alert" or aria-live="polite" for submit failuresprefers-reduced-motion disables carousels auto-playingalt="" or aria-hidden="true"@media (prefers-reduced-motion: reduce) {
.carousel {
animation: none;
}
}
Automated tools catch missing labels. They will not catch a focus trap. A human keyboard pass before release is still required.
Accessible HTML is not a compliance sticker. It is the same structure that makes SEO, mobile usability, and design QA easier. Build it into the component library and most pages inherit it.