Accessibility 101
Semantic first, ARIA second, keyboard always.
Accessibility (often shortened to a11y, because there are 11 letters between the "a" and the "y") is the practice of building sites that work for everyone, including people who can't see the screen, can't use a mouse, or can't process moving images. It's not a nice-to-have. In many countries it's a legal requirement. And it makes your site better for everyone, not just users with disabilities.
Rule one: semantic HTML first
90% of accessibility is just using the right element. A <button>is keyboard-accessible, focusable, clickable with Enter and Space, and announced as "button" by screen readers. A <div onClick>is none of those. You'd have to reinvent all that behavior in JS, badly.
<!-- Wrong: invisible to keyboard and screen reader users -->
<div onclick="submit()">Submit</div>
<!-- Right: works for everyone -->
<button type="submit">Submit</button><button>; you don't need role="button" on a div.ARIA: when semantics aren't enough
ARIA (Accessible Rich Internet Applications) is a set of HTML attributes that fills the gaps. You reach for ARIA when you're building something HTML doesn't have a native element for: a tabset, a tree view, a custom combobox.
role- what kind of widget is this?role="tab",role="dialog", etc.aria-label- invisible label. Use on icon-only buttons:<button aria-label="Close">ā</button>.aria-labelledby- point at another element that labels this one.aria-describedby- additional context. E.g. a form field pointing at error text.aria-hidden="true"- hide from assistive tech. Good for decorative icons next to text.aria-live- announce dynamic changes (notifications, validation errors).
<!-- Icon button needs an accessible name -->
<button aria-label="Close dialog">
<svg aria-hidden="true">...</svg>
</button>
<!-- Form field with help text -->
<label for="email">Email</label>
<input id="email" aria-describedby="email-hint" />
<p id="email-hint">We'll never share your email.</p>
<!-- Live region for status messages -->
<p aria-live="polite" id="status"></p>Keyboard navigation
Many people navigate the web entirely with the keyboard: power users, people with motor disabilities, screen reader users. Your site should be fully usable without ever touching the mouse.
- Tab moves to the next focusable element. Shift+Tab goes back.
- Enter activates links and buttons. Space also activates buttons (and scrolls pages).
- Arrows move within composite widgets: radio groups, menus, dropdowns, sliders.
- Escape closes modals, popovers, menus.
Try this on your own site right now: press Tab repeatedly. Can you reach every interactive element? Can you see where focus is? If not, you have an accessibility bug.
Focus management
When something appears on the screen (a modal, an error), focus often needs to move with it. When something is removed, focus must land somewhere predictable. The patterns:
- Modal opens: move focus inside the modal. Trap it there (Tab cycles within the modal). When it closes, return focus to the element that opened it.
- Error appears: announce it (use
role="alert"or anaria-liveregion), and optionally focus the field that caused it. - Route change in SPA: move focus to the main heading. Otherwise screen reader users have no signal that the page changed.
Color contrast
Light grey text on a white background looks elegantto designers. It's also unreadable for users with low vision, dim screens, or sunlight glare. The WCAG (Web Content Accessibility Guidelines) sets minimums:
- 4.5:1 contrast ratio for normal text.
- 3:1 for large text (18pt or 14pt bold).
- 3:1 for UI components and graphical elements.
Tools like the Chrome DevTools color picker show the contrast ratio and tell you if you pass.
prefers-reduced-motion
Some users get nausea or vertigo from animation. They set an OS-level preference. Honor it.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}This isn't a full solution (some animations carry meaning and need different fallbacks), but it's a strong default.
Screen reader basics
You've been writing about accessibility without seeing it used. Fix that today:
- VoiceOver (macOS/iOS): turn on with
Cmd+F5. PressControl+Option+Right Arrowto move through the page. - NVDA (Windows): free download. The most common screen reader.
- TalkBack (Android): turn on in accessibility settings.
Spend 10 minutes navigating your own site with eyes closed and the screen reader on. You'll discover bugs no amount of code review finds.
Tools for the everyday workflow
- axe DevTools - browser extension. Free. Catches most common WCAG violations automatically.
- Lighthouse - built into Chrome DevTools. Run an accessibility audit alongside performance.
- Wave - visual overlay highlighting issues directly on the page.
- Keyboard alone - the cheapest, fastest, and most revealing tool you have.
Common mistakes (a checklist)
- Using
<div onClick>instead of<button>. - Skipping
alton images or filling it with junk like "image". - Removing focus outlines without a replacement.
- Using color alone to convey meaning (red error text with no icon or label).
- Putting placeholder where a label belongs.
- Carousels and animations that can't be paused.
- Inaccessible custom controls when a native one would work.
- Heading levels chosen by visual size rather than structure.
- Forms where errors aren't announced to screen readers.
- Tiny tap targets (under 44x44 px) on mobile.
Quick quiz
You want a clickable icon-only X to close a modal. What's the most accessible markup?
Recap
- Semantic HTML first. ARIA second, only when needed.
- Everything interactive must be reachable and operable by keyboard alone. Visible focus rings, predictable order, Escape to dismiss.
- Move focus into modals when they open; return it when they close. Announce dynamic changes via
aria-live. - WCAG contrast minimums: 4.5:1 for normal text, 3:1 for large text and UI.
- Honor
prefers-reduced-motion. Captions on videos. Real labels on inputs. - Use axe DevTools and Lighthouse. Better yet: try your site with a screen reader.