Every time someone publishes an "accessibility checklist" with 80 items, two things happen. Readers give up. And the items that actually matter get buried under WCAG minutiae.
Accessibility has a real 80/20. About twenty fixes cover eighty percent of real-world issues. Here is the list I use for client projects, focused on impact instead of checkboxes.
The high-leverage fixes
These are the things I find on almost every audit and that make an immediate difference for users.
1. Use semantic HTML
The single highest-leverage fix. A <button> gives you:
- Keyboard activation (Enter and Space)
- Focus ring by default
- Screen reader announcement ("button")
disabledstate support
A <div onClick> gives you none of that. Every interactive element should be a <button>, <a>, <input>, <select>, or <textarea> unless you have a very specific reason.
// Bad
<div onClick={onClick} className="cursor-pointer">Submit</div>
// Good
<button onClick={onClick}>Submit</button>This one rule fixes an enormous percentage of accessibility issues.
2. Label every input
The most common failure I see: form inputs without labels.
// Bad — screen reader announces "edit text, blank"
<input type="email" placeholder="Email" />
// Good
<label htmlFor="email">Email</label>
<input id="email" type="email" />
// Also good — wrapping label
<label>
Email
<input type="email" />
</label>Placeholders are not labels. They disappear on focus and are read inconsistently by screen readers.
3. Make sure keyboard navigation works
Unplug your mouse. Try to:
- Reach every interactive element with Tab
- Activate buttons and links with Enter or Space
- Navigate form fields with Tab and Shift+Tab
- Close modals with Escape
- Skip to main content with a skip link
If you cannot do these, your keyboard support is broken. Fix the underlying semantic HTML before reaching for ARIA.
4. Check color contrast
Use the WCAG AA standard: 4.5:1 for normal text, 3:1 for large text and UI components.
The most common contrast issues:
- Grey text on white (designers love it, screen readers hate it)
- White text on light accent colors
- Placeholder text in form fields
Run your dev build through axe-core or Wave. Both flag contrast issues automatically.
5. Write meaningful link text
// Bad — screen reader user hears "click here, click here, click here"
<a href="/docs">Click here</a>
// Good — context travels with the link
<a href="/docs">Read the deployment docs</a>Screen reader users navigate by skipping between links. "Click here" tells them nothing out of context.
Focus management
Modals
When a modal opens, focus should move to the first interactive element inside it. When it closes, focus returns to the element that triggered it.
Without this, keyboard users get stuck. They Tab through the page behind the modal, unable to see where their focus is.
If you use a modal library (Radix, Headless UI, React Aria), this is handled for you. If you wrote your own, you need to implement it.
Route changes
In a single-page app, a route change does not move focus. A screen reader user clicks "About", the page changes, but their focus stays on the "About" link. They have no idea the content updated.
Fix: move focus to the new page's main heading on route change.
useEffect(() => {
document.querySelector('h1')?.focus()
}, [pathname])Make the h1 focusable with tabIndex={-1}.
Skip links
A skip link at the top of the page lets keyboard users jump past the navigation:
<a href="#main" className="sr-only focus:not-sr-only">
Skip to main content
</a>
<main id="main">
{/* ... */}
</main>This is a tiny addition that makes a huge difference for keyboard users.
ARIA: use sparingly
The ARIA mantra: no ARIA is better than bad ARIA.
ARIA does not fix broken semantics. It adds metadata to elements. If you used semantic HTML correctly, you usually do not need ARIA.
// Bad — using ARIA to fake a button
<div role="button" tabindex="0" onClick={onClick}>Submit</div>
// Good — let HTML do its job
<button onClick={onClick}>Submit</button>Common ARIA patterns that are legitimate:
aria-labelon icon-only buttonsaria-livefor dynamic notificationsaria-expandedon disclosure buttonsrole="dialog"andaria-modalfor custom modals (if you cannot use a library)
Everything else: be skeptical.
Testing workflow
Three layers, in order of cost:
Layer 1: Automated (axe-core)
Add to your test suite:
import { axe } from 'vitest-axe'
import { render } from '@testing-library/react'
it('has no accessibility violations', async () => {
const { container } = render(<MyComponent />)
const results = await axe(container)
expect(results).toHaveNoViolations()
})Catches 40-60% of issues. Fast, free, repeatable. Do this in CI on day one.
Layer 2: Manual keyboard testing
Unplug your mouse. Use the site. You will find issues axe cannot catch, like focus traps that do not work, keyboard shortcuts that conflict, and navigation that requires hover states.
Layer 3: Screen reader testing
Use VoiceOver (Cmd+F5 on macOS) or NVDA (free on Windows). You do not need to be a power user. Learn the top five commands and test your site. Most issues are obvious within minutes.
What to ignore
Things that get a lot of attention but rarely matter:
- Perfect WCAG AAA compliance — AA is the legal and practical target.
- Custom focus rings on every element — the browser default is fine for most cases. Customize only when the design truly needs it.
- Switching to 10px minimum font — accessibility best practice says 16px minimum for body text. Anything less is hard to read.
- ARIA on everything — adds noise, fixes nothing if the underlying HTML is wrong.
Accessibility is not a checklist. It is a habit of using semantic HTML, labeling inputs, and testing with a keyboard. The 80/20 is real — focus on the high-leverage fixes first.
Want an accessibility audit?
I audit React and Next.js apps for accessibility, fix the high-impact issues, and set up automated testing in CI. Let's talk.
Frequently Asked Questions
What is the most common web accessibility mistake?
Using div or span elements with onClick handlers instead of semantic button or a elements. This breaks keyboard navigation, screen reader announcements, and focus handling. Use real HTML elements whenever possible.
Is WCAG compliance required by law?
In many jurisdictions, yes. The Americans with Disabilities Act in the US and the European Accessibility Act apply to websites. Lawsuits around web accessibility are increasing year over year, especially against e-commerce sites.
What does axe-core check?
axe-core is an open-source accessibility testing engine that runs in your browser or test suite. It catches about 40-60% of WCAG violations automatically, including missing labels, color contrast issues, missing ARIA, and broken landmarks.
How do I test a website with a screen reader?
On macOS, enable VoiceOver with Cmd+F5 and use the VoiceOver navigation commands. On Windows, install NVDA (free). Learn the top 5 commands: next element, activate, headings list, forms list, read from here. Test your site, do not try to become a power user.