Skip to content
Development12 min read

WCAG 2.2 Checklist for Developers

A practical checklist of WCAG 2.2 Level A and AA success criteria with code examples. Bookmark this page and reference it during development and code reviews.

About This Checklist

WCAG 2.2, published in October 2023, is the latest version of the Web Content Accessibility Guidelines. It builds on WCAG 2.1 by adding 9 new success criteria, primarily focused on improving accessibility for users with cognitive disabilities, low vision, and motor impairments.

This checklist covers the most important Level A and AA criteria that web developers encounter daily. Each criterion includes a description and, where applicable, a code example showing the correct implementation. This is not exhaustive — refer to the official WCAG 2.2 specification for the complete list of 87 success criteria.

Criteria marked as new in WCAG 2.2 are highlighted. If you are already compliant with WCAG 2.1, pay special attention to these additions: 2.4.11 Focus Not Obscured, 2.5.7 Dragging Movements, 2.5.8 Target Size, 3.3.7 Redundant Entry, and 3.3.8 Accessible Authentication.

1. Perceivable

Level A

1.1.1 Non-text Content

All images, icons, and non-text content must have text alternatives that serve the equivalent purpose.

<!-- Informative image -->
<img src="chart.png" alt="Q3 revenue grew 25% to $1.2M">

<!-- Decorative image -->
<img src="divider.svg" alt="" role="presentation">

<!-- Icon button -->
<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>
Level A

1.3.1 Info and Relationships

Information, structure, and relationships conveyed visually must also be conveyed programmatically through semantic HTML.

<!-- Use semantic headings -->
<h1>Page Title</h1>
<h2>Section Title</h2>

<!-- Use proper list markup -->
<ul>
  <li>Item one</li>
  <li>Item two</li>
</ul>

<!-- Associate labels with inputs -->
<label for="email">Email</label>
<input id="email" type="email">
Level AA

1.3.5 Identify Input Purpose

Form fields that collect personal information must use autocomplete attributes to identify their purpose.

<input type="text" autocomplete="name" name="fullname">
<input type="email" autocomplete="email" name="email">
<input type="tel" autocomplete="tel" name="phone">
<input type="text" autocomplete="street-address" name="address">
Level A

1.4.1 Use of Color

Color must not be the only visual means of conveying information. Use additional indicators like icons, text, or patterns.

<!-- Bad: color only -->
<span style="color: red">Error</span>

<!-- Good: color + icon + text -->
<span style="color: red" role="alert">
  <svg aria-hidden="true"><!-- error icon --></svg>
  Error: Email is required
</span>
Level AA

1.4.3 Contrast (Minimum)

Text must have a contrast ratio of at least 4.5:1 against its background (3:1 for large text at 18pt+ or 14pt+ bold).

/* Good contrast - passes AA */
.text-primary {
  color: #1e40af;        /* Blue - 8.6:1 on white */
  background: #ffffff;
}

/* Large text - 3:1 minimum */
h1 {
  color: #6b7280;        /* Gray - 4.6:1 on white */
  font-size: 24px;
  font-weight: bold;
}
Level AA

1.4.4 Resize Text

Text must be resizable up to 200% without loss of content or functionality. Use relative units instead of fixed pixels.

/* Use rem/em instead of px */
body { font-size: 1rem; }   /* 16px base */
h1 { font-size: 2.5rem; }   /* Scales with user settings */
p { line-height: 1.5; }     /* Relative line height */

/* Avoid fixed heights on text containers */
.card { min-height: 5rem; } /* Not: height: 80px */
Level AA

1.4.11 Non-text Contrast

UI components and graphical objects must have a contrast ratio of at least 3:1 against adjacent colors.

/* Buttons need 3:1 contrast for borders/backgrounds */
.btn {
  border: 2px solid #6b7280; /* 4.6:1 on white */
  background: #2563eb;       /* 5.2:1 on white */
}

/* Focus indicators */
:focus-visible {
  outline: 3px solid #2563eb; /* 5.2:1 */
  outline-offset: 2px;
}

2. Operable

Level A

2.1.1 Keyboard

All functionality must be operable through a keyboard interface. No keyboard traps — users must be able to navigate away from any component.

<!-- Use native interactive elements -->
<button onclick="handleClick()">Submit</button>

<!-- If using div, add keyboard support -->
<div
  role="button"
  tabindex="0"
  onkeydown="if(e.key==='Enter'||e.key===' ') handleClick()"
  onclick="handleClick()"
>
  Submit
</div>
Level A

2.4.1 Bypass Blocks

Provide a mechanism to skip repetitive navigation blocks and jump directly to the main content.

<!-- Skip link as first focusable element -->
<body>
  <a href="#main" class="sr-only focus:not-sr-only">
    Skip to main content
  </a>
  <nav>...</nav>
  <main id="main">...</main>
</body>
Level A

2.4.2 Page Titled

Every page must have a descriptive title that identifies its topic or purpose.

<!-- Descriptive and unique per page -->
<title>WCAG 2.2 Checklist | FixMyWeb Blog</title>

<!-- In Next.js -->
export const metadata = {
  title: "WCAG 2.2 Checklist for Developers",
};
Level A

2.4.4 Link Purpose (In Context)

The purpose of each link must be determinable from the link text alone, or from the link text together with its surrounding context.

<!-- Bad -->
<a href="/report">Click here</a>

<!-- Good -->
<a href="/report">View accessibility report</a>

<!-- Or use aria-label for context -->
<a href="/report" aria-label="View accessibility report for example.com">
  View report
</a>
Level AA

2.4.7 Focus Visible

Keyboard focus indicators must be visible. Never remove outlines without providing an alternative focus style.

/* Never do this without a replacement */
/* :focus { outline: none; } */

/* Good: custom focus indicator */
:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 2px;
  border-radius: 4px;
}

/* Remove outline only for mouse users */
:focus:not(:focus-visible) {
  outline: none;
}
Level AA

2.4.11 Focus Not Obscured (Minimum)

When a component receives keyboard focus, it must not be entirely hidden by other content such as sticky headers, footers, or modals. This is new in WCAG 2.2.

/* Ensure focused elements are visible */
.sticky-header {
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Add scroll margin to account for sticky header */
:target,
[tabindex]:focus {
  scroll-margin-top: 5rem; /* height of sticky header */
}
Level AA

2.5.7 Dragging Movements

Any functionality that requires dragging must also be operable through a single pointer without dragging. New in WCAG 2.2.

<!-- Sortable list: support both drag and buttons -->
<li draggable="true">
  <span>Item text</span>
  <button aria-label="Move up">&#9650;</button>
  <button aria-label="Move down">&#9660;</button>
</li>
Level AA

2.5.8 Target Size (Minimum)

Interactive targets must be at least 24x24 CSS pixels, unless the target is inline text or there is an equivalent larger target. New in WCAG 2.2.

/* Minimum 24x24px target size */
button, a, [role="button"] {
  min-width: 24px;
  min-height: 24px;
}

/* Better: 44x44px for touch targets */
.touch-target {
  min-width: 44px;
  min-height: 44px;
  padding: 12px;
}

3. Understandable

Level A

3.1.1 Language of Page

The default human language of each page must be specified in the HTML lang attribute.

<html lang="en">
<!-- For French: <html lang="fr"> -->
<!-- For Spanish: <html lang="es"> -->

<!-- Language of parts (for mixed content) -->
<p>The French word <span lang="fr">bonjour</span> means hello.</p>
Level A

3.2.1 On Focus

When a component receives focus, it must not trigger an unexpected change of context such as submitting a form, opening a new window, or moving focus to a different component.

Level A

3.3.1 Error Identification

When an input error is detected, the item in error must be identified and the error described in text.

<div>
  <label for="email">Email address</label>
  <input
    id="email"
    type="email"
    aria-invalid="true"
    aria-describedby="email-error"
  >
  <p id="email-error" role="alert" class="text-red-600">
    Please enter a valid email address (e.g., user@example.com)
  </p>
</div>
Level A

3.3.2 Labels or Instructions

Labels or instructions must be provided when content requires user input. Every form field needs a visible, associated label.

<!-- Visible label associated via for/id -->
<label for="phone">Phone number (optional)</label>
<input id="phone" type="tel" autocomplete="tel">

<!-- Required field indicator -->
<label for="name">
  Full name <span aria-hidden="true">*</span>
  <span class="sr-only">(required)</span>
</label>
<input id="name" type="text" required aria-required="true">
Level A

3.3.7 Redundant Entry

Information previously entered or provided by the user must be auto-populated or available for selection, unless re-entering is essential for security. New in WCAG 2.2.

Level AA

3.3.8 Accessible Authentication (Minimum)

Authentication must not require cognitive function tests (like remembering a password or solving a puzzle) unless an alternative method is available. New in WCAG 2.2.

<!-- Support password managers -->
<input
  type="password"
  autocomplete="current-password"
  name="password"
>

<!-- Provide alternatives to CAPTCHAs -->
<!-- Option 1: Allow paste into verification fields -->
<!-- Option 2: Email/SMS verification link -->
<!-- Option 3: Biometric authentication -->

4. Robust

Level A

4.1.2 Name, Role, Value

All user interface components must have accessible names and roles. Custom widgets must expose their state to assistive technologies.

<!-- Toggle button with state -->
<button
  aria-pressed="true"
  aria-label="Dark mode"
>
  Toggle Theme
</button>

<!-- Custom select/combobox -->
<div
  role="combobox"
  aria-expanded="false"
  aria-haspopup="listbox"
  aria-label="Select country"
>
  <input type="text" aria-autocomplete="list">
  <ul role="listbox" id="country-list">
    <li role="option" id="us">United States</li>
    <li role="option" id="uk">United Kingdom</li>
  </ul>
</div>
Level AA

4.1.3 Status Messages

Status messages (like form submission confirmations, error counts, or search results) must be announced to screen readers without receiving focus.

<!-- Live region for status updates -->
<div role="status" aria-live="polite">
  3 results found
</div>

<!-- Alert for errors -->
<div role="alert">
  Form submission failed. Please check the errors above.
</div>

<!-- Loading indicator -->
<div role="status" aria-live="polite">
  <span class="sr-only">Loading results...</span>
  <svg class="animate-spin" aria-hidden="true">...</svg>
</div>

Testing Your Implementation

After implementing these criteria, validate your work with a combination of automated and manual testing:

  1. Automated scanning — Run your site through FixMyWeb to catch technical violations like missing alt text, contrast issues, and invalid ARIA.
  2. Keyboard testing — Tab through every page. Verify all interactive elements are reachable, have visible focus indicators, and do not trap the keyboard.
  3. Screen reader testing — Test with at least one screen reader. NVDA (free, Windows), VoiceOver (built-in, macOS/iOS), or TalkBack (built-in, Android).
  4. Zoom testing — Zoom your browser to 200% and verify no content is lost, truncated, or overlapping.
  5. Color contrast — Use the FixMyWeb Contrast Checker to verify all text-background combinations.

Integrating Accessibility into Your Workflow

The most effective approach to accessibility is preventing issues rather than fixing them after the fact. Consider these practices:

  • Add accessibility checks to your CI/CD pipeline using FixMyWeb's API
  • Include accessibility criteria in your definition of done
  • Conduct accessibility-focused code reviews
  • Use ESLint plugins like eslint-plugin-jsx-a11y for React projects
  • Test with real assistive technology users when possible

Resources

Validate Your WCAG Compliance

Run a free scan to check how many of these criteria your website currently meets.

Scan Your Website Free