UseToolSuite UseToolSuite

Web Accessibility Checklist: A Practical Guide for Developers

A no-nonsense web accessibility checklist for developers. Covers WCAG 2.1 essentials, semantic HTML, keyboard navigation, color contrast, screen readers, ARIA, and automated testing tools.

Necmeddin Cunedioglu Necmeddin Cunedioglu 13 min read
Part of the CSS Color Systems: A Complete Guide for Developers series

Practice what you learn

Color Converter

Try it free →

I shipped a client project last year that looked beautiful. Smooth animations, perfect color palette, custom fonts. Then the client’s accessibility audit came back: 47 violations. I’d built a site that 15% of the world’s population couldn’t use properly.

Accessibility isn’t a nice-to-have — it’s a legal requirement in many jurisdictions and, honestly, it’s just good engineering. Here’s the checklist I now run through on every project.

Understanding WCAG 2.1: The Standards Behind Accessibility

The Web Content Accessibility Guidelines (WCAG) are the international standard for web accessibility, maintained by the W3C’s Web Accessibility Initiative (WAI). WCAG 2.1 organizes its success criteria into three conformance levels, each building upon the previous one.

LevelWhat It CoversWho Needs ItExample Criteria
ABasic accessibility (alt text, keyboard access, no seizure-inducing content)Everyone — bare minimumText alternatives, keyboard access, no auto-playing audio
AAEnhanced accessibility (contrast ratios, text resizing, consistent navigation)Most legal requirements (ADA, EAA, Section 508)4.5:1 contrast ratio, text resizeable to 200%, visible focus indicators
AAAMaximum accessibility (sign language, extended audio descriptions, enhanced contrast)Specialized needs, government portals7:1 contrast ratio, no timing limits, pronunciation support

Target Level AA. It’s what the Americans with Disabilities Act (ADA), the European Accessibility Act (EAA), and Section 508 reference. Achieving AAA across an entire site is often impractical for most applications, but individual AAA criteria can be valuable to implement selectively.

WCAG’s Four Principles: POUR

Every WCAG criterion falls under one of four principles, commonly remembered by the acronym POUR:

PrincipleMeaningKey Question
PerceivableUsers must be able to perceive the contentCan users see, hear, or touch all information?
OperableUsers must be able to operate the interfaceCan users navigate and interact with all controls?
UnderstandableUsers must understand the information and interfaceIs the content readable and predictable?
RobustContent must work with current and future technologiesDoes it work with assistive technologies?

Semantic HTML: The Foundation of Accessible Web Pages

Using the right HTML elements is the single highest-impact accessibility improvement you can make. Screen readers and other assistive technologies rely entirely on semantic meaning to convey page structure to users.

Use Native Elements Instead of Generic DIVs

The most common accessibility mistake is building custom interactive elements from <div> and <span> tags. Native HTML elements carry built-in semantics, keyboard behavior, and ARIA roles that custom elements do not.

<!-- Bad: screen readers don't know this is navigation -->
<div class="nav">
  <div class="nav-item" onclick="navigate('/')">Home</div>
</div>

<!-- Good: screen readers announce "navigation" -->
<nav aria-label="Main navigation">
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>

When you use <nav>, screen readers automatically announce “navigation landmark.” When you use <a>, the element is automatically focusable, activatable with Enter, and announced as a link with its destination. A <div> with onclick provides none of these behaviors — you would need to manually add role="link", tabindex="0", keyboard event listeners, and focus styling to achieve what <a> gives you for free.

Heading Hierarchy: The Document Outline

Headings create the document outline that screen reader users navigate by. Many screen reader users scan a page by jumping between headings (pressing H in NVDA or JAWS) before reading content, much like sighted users scan visual headings.

<!-- Bad: heading levels skipped, misleading structure -->
<h1>Page Title</h1>
<h4>Looks right visually but breaks the outline</h4>

<!-- Good: proper hierarchy, use CSS for visual sizing -->
<h1>Page Title</h1>
<h2>Section Title</h2>
<h3>Subsection</h3>

Rules for headings:

  • One <h1> per page — it should describe the page’s primary purpose
  • Never skip heading levels (e.g., don’t jump from <h2> to <h4>)
  • Use CSS classes for visual sizing — don’t choose a heading level based on its default font size
  • Headings should be descriptive, not generic (use “Shipping Options” not “Options”)

HTML5 Landmark Elements

Landmark elements define the major regions of a page. Screen readers allow users to jump directly between landmarks, providing efficient navigation for long pages.

ElementImplicit ARIA RolePurposeUsage Notes
<header>bannerSite header with logo and global navigationOnly the top-level <header> gets banner role
<nav>navigationNavigation linksUse aria-label to distinguish multiple navs
<main>mainPrimary content areaExactly one per page
<aside>complementarySidebar or tangentially related contentShould make sense independently
<footer>contentinfoSite footer with legal links and contact infoOnly the top-level <footer> gets this role
<article>articleSelf-contained, independently distributable contentBlog posts, news articles, forum posts
<section>regionThematic grouping of contentOnly gets region role if it has an accessible name

Keyboard Navigation: Making Every Control Accessible

Every interactive element on your page must be fully operable with a keyboard alone. Approximately 2-3% of web users rely exclusively on keyboard navigation due to motor disabilities, temporary injuries, or personal preference.

Focus Indicators: Making the Current Element Visible

When a user tabs through your page, they need a clear visual indicator of which element is currently focused. Removing focus outlines is one of the most harmful accessibility mistakes developers make.

/* Never do this globally — it removes all focus indicators */
*:focus { outline: none; }

/* Instead, use :focus-visible for smart focus management */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Optionally hide focus ring for mouse users (already handled by :focus-visible) */
:focus:not(:focus-visible) {
  outline: none;
}

The :focus-visible pseudo-class is the modern best practice — it shows the focus ring for keyboard navigation but hides it for mouse clicks, giving both user groups an optimal experience.

Tab Order and Interactive Element Rules

ElementFocusable by DefaultKeyboard ActivationNotes
<a href>✅ YesEnterMust have href to be focusable
<button>✅ YesEnter or SpacePreferred for actions that don’t navigate
<input />✅ YesVaries by typeUse appropriate type attributes
<select>✅ YesSpace to open, arrows to navigateArrow keys move between options
<textarea>✅ YesN/A (text input)Tab moves focus away, not inserting a tab character
<div>❌ NoN/ANever use for interactive elements
<span>❌ NoN/ANever use for interactive elements
<!-- Bad: not keyboard-accessible without significant extra work -->
<div class="button" onclick="submit()">Submit</div>

<!-- Good: handles Enter, Space, focus, and screen reader announcement automatically -->
<button type="submit">Submit</button>

Critical rule: Never use positive tabindex values (e.g., tabindex="5"). They override the natural DOM order and create unpredictable, confusing tab sequences. Only use tabindex="0" (to make an element focusable in DOM order) or tabindex="-1" (to make it programmatically focusable but not in the tab order).

For pages with extensive navigation menus, provide a “skip to main content” link as the first focusable element. This allows keyboard users to bypass repetitive navigation on every page.

<body>
  <a href="#main-content" class="skip-link">Skip to main content</a>
  <nav><!-- extensive navigation --></nav>
  <main id="main-content"><!-- page content --></main>
</body>
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  padding: 0.5rem 1rem;
  background: var(--color-accent);
  color: white;
  z-index: 100;
}
.skip-link:focus {
  top: 0;
}

Color and Contrast: Ensuring Visual Accessibility

Color-related accessibility issues affect a large portion of users. Approximately 8% of men and 0.5% of women have some form of color vision deficiency. Beyond color blindness, many users work in challenging lighting conditions — bright sunlight, dim rooms, or low-quality displays — where contrast becomes critical.

WCAG Contrast Requirements

Element TypeMinimum Ratio (Level AA)Enhanced Ratio (Level AAA)How to Measure
Normal text (< 18pt)4.5:17:1Foreground vs. background color
Large text (≥ 18pt or ≥ 14pt bold)3:14.5:1Foreground vs. background color
UI components and graphical objects3:1Not specifiedComponent vs. adjacent color
Non-text contrast (icons, borders)3:1Not specifiedElement vs. background

Check your colors: Use our Color Converter to get exact RGB values and calculate contrast ratios. Our Color Palette Generator creates harmonious schemes — just verify each combination meets the WCAG ratios listed above.

Don’t Rely on Color Alone

Information conveyed through color must also be available through other visual means — text labels, icons, patterns, or shapes. This is WCAG criterion 1.4.1 and one of the most commonly violated.

<!-- Bad: only color indicates the error state -->
<input style="border-color: red;" />

<!-- Good: color + icon + descriptive text -->
<input aria-invalid="true" aria-describedby="email-error" />
<span id="email-error" role="alert">
  ⚠️ Please enter a valid email address
</span>

Common violations to watch for:

  • Form validation that only changes border color on error
  • Charts and graphs that distinguish data series only by color
  • Links that are the same color as surrounding text with no underline
  • Status indicators (online/offline) using only green/red dots without labels

Images, Media, and Alternative Content

Alt Text: The Rules for Every Image Type

Every <img /> element must have an alt attribute, but the content of that attribute depends on the image’s purpose.

Image TypeAlt Text StrategyExample
InformativeDescribe the content or messagealt="Sales revenue increased 40% in Q3 2025"
FunctionalDescribe the action or destinationalt="Search", alt="Download PDF report"
DecorativeEmpty alt attributealt="" (not omitted — explicitly empty)
Complex (charts, diagrams)Brief alt + longer descriptionalt="Q3 revenue chart" with aria-describedby linking to a detailed text description
Text in imageReproduce the text exactlyalt="50% OFF — Summer Sale"
<!-- Informative image -->
<img src="chart.png" alt="Sales increased 40% in Q3, reaching $2.4M" />

<!-- Functional image (inside a link) -->
<a href="/search"><img src="search-icon.png" alt="Search" /></a>

<!-- Decorative image (adds no information) -->
<img src="decorative-border.png" alt="" />

<!-- Complex image with extended description -->
<figure>
  <img src="architecture.png" alt="System architecture diagram" aria-describedby="arch-desc" />
  <figcaption id="arch-desc">
    The system consists of three layers: a React frontend communicating via REST API
    with a Node.js backend, which connects to a PostgreSQL database...
  </figcaption>
</figure>

Video and Audio Accessibility

Video content requires captions for deaf and hard-of-hearing users, and audio descriptions for blind users. Audio-only content requires text transcripts.

<video controls>
  <source src="demo.mp4" type="video/mp4" />
  <track kind="captions" src="captions-en.vtt" srclang="en" label="English" default />
  <track kind="descriptions" src="descriptions-en.vtt" srclang="en" label="Audio Descriptions" />
</video>

ARIA: Bridging the Gap When HTML Falls Short

The first rule of ARIA (Accessible Rich Internet Applications) is: don’t use ARIA if a native HTML element already provides the semantics you need. ARIA adds semantic information for assistive technologies, but it does not add behavior — you still need to implement keyboard interaction, focus management, and visual states yourself.

Essential ARIA Patterns for Web Applications

PatternARIA AttributesWhen to Use
Live regionsaria-live="polite" or aria-live="assertive"Dynamic content updates (notifications, chat messages, form validation)
Expanded/collapsedaria-expanded="true/false", aria-controls="id"Accordions, dropdown menus, collapsible panels
Current pagearia-current="page"Highlighting the active page in navigation
Described byaria-describedby="id"Connecting form inputs to help text or error messages
Required fieldsaria-required="true"Form fields that must be filled (prefer HTML required attribute)
Disabled statearia-disabled="true"Elements that are visible but not interactive
Modal dialogsrole="dialog", aria-modal="true", aria-labelledby="id"Modal windows that trap focus
<!-- Live region: polite announcement (waits for user to finish reading) -->
<div aria-live="polite" aria-atomic="true">
  3 items added to cart — Total: $45.99
</div>

<!-- Icon button with accessible label -->
<button aria-label="Close dialog" type="button">
  <svg aria-hidden="true"><!-- X icon --></svg>
</button>

<!-- Accordion with expanded/collapsed state -->
<button aria-expanded="false" aria-controls="faq-answer-1">
  What is your return policy?
</button>
<div id="faq-answer-1" hidden>
  We offer a 30-day return policy for all unused items...
</div>

<!-- Navigation with current page indicator -->
<nav aria-label="Main">
  <a href="/" aria-current="page">Home</a>
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
</nav>

Common ARIA Mistakes

MistakeWhy It’s WrongFix
role="button" on a <div>Missing keyboard support and focusUse <button> instead
aria-label on a non-interactive <div>Screen readers may not announce itUse visible text or <p>
aria-hidden="true" on focusable elementsCreates a focus trap for screen reader usersRemove from tab order first
Duplicate aria-label and visible textScreen readers announce both, causing confusionUse one or the other

Forms: Accessible Input Patterns

Forms are one of the most interaction-heavy parts of any web application, and they are where accessibility issues cause the most user frustration.

Label Association

Every form input must have a programmatically associated label. There are two valid methods:

<!-- Method 1: Wrapping (implicit association) -->
<label>
  Email address
  <input type="email" name="email" required />
</label>

<!-- Method 2: for/id (explicit association — preferred) -->
<label for="user-email">Email address</label>
<input type="email" id="user-email" name="email" required />

Error Handling and Validation Feedback

When form validation fails, users need to know what went wrong, which field has the error, and how to fix it. This information must be available to both sighted users and screen reader users.

<label for="password">Password</label>
<input
  type="password"
  id="password"
  aria-invalid="true"
  aria-describedby="password-error password-requirements"
  required
/>
<p id="password-error" role="alert" class="error">
  Password must be at least 8 characters long
</p>
<p id="password-requirements" class="help-text">
  Include at least one uppercase letter, one number, and one special character
</p>

Testing: A Multi-Layered Approach

No single tool catches all accessibility issues. Automated tools catch approximately 30-40% of WCAG violations. The remaining 60-70% require manual testing and user testing.

Testing Strategy by Method

MethodCoverageToolsWhat It Catches
Automated scanning~30-40%axe DevTools, Lighthouse, WAVE, eslint-plugin-jsx-a11yMissing alt text, contrast failures, missing labels, duplicate IDs
Keyboard testing~20%Unplug your mouse, use Tab/Shift+Tab/Enter/Space/EscapeFocus traps, unreachable elements, invisible focus indicators, incorrect tab order
Screen reader testing~20%VoiceOver (macOS), NVDA (Windows), TalkBack (Android)Missing announcements, confusing reading order, unlabeled controls
Visual inspection~10%Browser zoom to 200%, Windows High Contrast Mode, forced colorsLayout breakage at zoom, information lost in high contrast, text truncation
User testingRemaining gapsReal users with disabilitiesCognitive issues, unexpected workflows, real-world usability problems

Essential Manual Test Procedures

Keyboard-only navigation test:

  1. Unplug your mouse or disable your trackpad
  2. Press Tab to move through all interactive elements — can you reach everything?
  3. Press Enter or Space on buttons and links — do they activate?
  4. Press Escape to close modals and menus — does focus return to the trigger?
  5. Check that focus order is logical (left to right, top to bottom)
  6. Verify focus indicators are visible on every focused element

200% zoom test:

  1. Zoom your browser to 200% (Ctrl + +)
  2. No content should require horizontal scrolling
  3. No text should be cut off or overlap other elements
  4. All interactive elements should remain usable

The Complete Accessibility Checklist

Use this checklist on every project before launch:

Structure and Semantics

  • One <h1> per page with proper heading hierarchy (no skipped levels)
  • Landmark elements used (<main>, <nav>, <header>, <footer>)
  • Page language declared (<html lang="en">)
  • Page title is descriptive and unique

Keyboard and Interaction

  • All interactive elements keyboard-accessible (Tab, Enter, Space, Escape)
  • Focus indicators visible on every focusable element (:focus-visible)
  • No keyboard traps (user can always Tab away from any element)
  • Skip navigation link provided for long pages
  • Touch targets at least 44×44px on mobile

Visual Design

  • Text color contrast meets 4.5:1 for normal text, 3:1 for large text
  • UI component contrast meets 3:1 against adjacent colors
  • Information not conveyed by color alone (use text, icons, or patterns)
  • Content readable and functional at 200% browser zoom
  • prefers-reduced-motion media query respected for animations

Images and Media

  • All images have appropriate alt attributes (informative, functional, or empty)
  • Complex images have extended descriptions
  • Videos have captions (closed or open)
  • Audio content has text transcripts

Forms

  • All form inputs have programmatically associated labels
  • Required fields are indicated (not just by color)
  • Error messages are descriptive, visible, and announced by screen readers (role="alert")
  • Form instructions appear before the form, not only after submission

Dynamic Content

  • Live regions announce dynamic content changes (aria-live)
  • Modal dialogs trap focus and return focus on close
  • Expanded/collapsed states communicated (aria-expanded)
  • Loading states communicated to screen readers

Further Reading


Building accessible interfaces? Use our Color Converter to verify contrast ratios, HTML Formatter for clean semantic markup, and Meta Tag Generator for accessible page metadata.

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author
13 min read
-- views

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.