DEV Community

Cover image for WCAG 2.2: What Changed, Why It Matters, and How to Implement It
Thoithoi Shougrakpam
Thoithoi Shougrakpam

Posted on

WCAG 2.2: What Changed, Why It Matters, and How to Implement It

Nine new success criteria. One removed. Here is what every frontend engineer needs to know.


WCAG 2.2 became an official W3C Recommendation on December 12, 2024. If your team is still targeting 2.1 as a compliance baseline, you are already behind. The W3C explicitly advises using 2.2 to maximize future applicability of accessibility efforts, and regulators in the EU, UK, and US are actively aligning their policies to the latest version.

This article covers every new success criterion using a consistent format: what the spec requires, why the criterion exists and who it protects, and how to implement it in practice.


What Was Removed First: 4.1.1 Parsing

Before the new criteria, one was cut. WCAG 2.2 removed 4.1.1 Parsing, which previously required well-formed HTML so assistive technologies could reliably parse it.

Why removed: Modern browsers and screen readers have become resilient enough to handle malformed markup without accessibility failures. The criterion no longer reliably predicted real-world accessibility outcomes, so the working group dropped it.

Practical note: If your organization is contractually obligated to WCAG 2.0 or 2.1 conformance, you may still need to test and report on 4.1.1 separately. For new 2.2 audits, it is gone.


The 9 New Success Criteria


1. Focus Not Obscured (Minimum) -- 2.4.11 -- Level AA

What: When a UI component receives keyboard focus, the focused element must not be entirely hidden by author-created content. Partially obscured is acceptable at this level. Entirely hidden is not.

Why: Users who navigate by keyboard (people with motor disabilities, switch device users, power users) need to see where focus is at all times. Sticky headers, floating cookie banners, fixed chat widgets, and bottom navigation bars are the most common offenders. When focus moves behind one of these layers and disappears completely, the user loses their place on the page with no visual cue for what is currently selected. This is especially disorienting for users with cognitive disabilities who are more sensitive to context loss during a task.

How to implement:

The core fix is ensuring scroll-padding-top accounts for any fixed header height so the browser scrolls enough to keep focused elements visible.

/* If your sticky header is 64px tall */
html {
  scroll-padding-top: 80px; /* header height + breathing room */
}

/* Alternatively, scoped to focusable elements */
a:focus,
button:focus,
[tabindex]:focus {
  scroll-margin-top: 80px;
}
Enter fullscreen mode Exit fullscreen mode

For dynamic header heights (collapsing navs, announcement banners that appear after load), update the value from JavaScript:

function updateScrollPadding() {
  const header = document.querySelector('.sticky-header');
  const height = header?.getBoundingClientRect().height ?? 0;
  document.documentElement.style.scrollPaddingTop = `${height + 16}px`;
}

window.addEventListener('resize', updateScrollPadding);
updateScrollPadding();
Enter fullscreen mode Exit fullscreen mode

Test it: Tab through your page with a sticky header visible. Every focused element should be at least partially visible above the fold.


2. Focus Not Obscured (Enhanced) -- 2.4.12 -- Level AAA

What: Same intent as 2.4.11, but stricter. The focused component must not be obscured at all, not even partially.

Why: At AA (2.4.11), a focused element that is 10% visible technically passes. For users with low vision who rely on high zoom levels or screen magnification, even partial obscuring can make the focus indicator undetectable in practice. The AAA version closes that gap entirely.

How to implement:

Everything from 2.4.11 applies. The additional requirement is that no part of the focused element is covered by overlapping author-created content. In practice this means:

  • scroll-padding values must fully clear the focused element above any sticky layers.
  • Fixed overlays (modals, drawers, sheets) must trap focus inside themselves while open, so keyboard focus can never land on content behind them.
// Trap focus inside an open modal
function trapFocus(modalElement) {
  const focusable = modalElement.querySelectorAll(
    'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  modalElement.addEventListener('keydown', (e) => {
    if (e.key !== 'Tab') return;
    if (e.shiftKey) {
      if (document.activeElement === first) {
        e.preventDefault();
        last.focus();
      }
    } else {
      if (document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

3. Focus Appearance -- 2.4.13 -- Level AAA

What: When a keyboard focus indicator is visible, it must meet specific size and contrast requirements. The focus indicator area must be at least the perimeter of the unfocused component multiplied by 2 CSS pixels. The contrast ratio between focused and unfocused states must be at least 3:1 against adjacent colors.

Why: Browser default focus outlines are frequently invisible against common backgrounds, and many codebases globally suppress them with outline: none (still a widespread anti-pattern). Users with low vision, cognitive disabilities, and anyone relying entirely on keyboard navigation depend on a focus indicator that is visually obvious, not just technically present. A faint, thin blue ring at low contrast does not serve these users.

How to implement:

The first step is removing the global outline: none pattern. If you need to suppress the browser ring for mouse users, use :focus-visible instead of :focus:

/* Wrong: removes focus ring for everyone, including keyboard users */
*:focus {
  outline: none;
}

/* Right: removes ring only when pointer (not keyboard) is in use */
*:focus:not(:focus-visible) {
  outline: none;
}

/* Custom focus indicator that satisfies AAA geometric and contrast requirements */
*:focus-visible {
  outline: 3px solid #0f62fe;
  outline-offset: 2px;
  border-radius: 2px;
}
Enter fullscreen mode Exit fullscreen mode

A practical shortcut: a 3px solid outline in a color with at least 3:1 contrast against the surrounding background satisfies the geometric requirement for most standard interactive components. For components on dark surfaces, check the contrast of your focus color against the dark background, not just the default page background.


4. Dragging Movements -- 2.5.7 -- Level AA

What: Any functionality that uses a dragging movement (click-and-drag, touch drag) must also be achievable with a single pointer action (click or tap) without dragging. Exceptions apply only when the drag is essential to the functionality itself.

Why: Dragging requires simultaneously pressing, holding, and moving a pointer. This compound gesture is unreliable or impossible for users with hand tremors, limited fine motor control, or motor disabilities affecting pointer precision. Sortable lists, kanban boards, sliders, map pan gestures, and date range pickers are common failure cases. The criterion does not prohibit drag interactions. It requires that a non-drag path exists to accomplish the same result.

How to implement:

For sortable lists, provide explicit move buttons alongside the drag handle:

function SortableItem({ item, onMoveUp, onMoveDown }) {
  return (
    <div draggable onDragStart={...} onDragEnd={...}>
      <span>{item.label}</span>
      <button aria-label={`Move ${item.label} up`} onClick={onMoveUp}></button>
      <button aria-label={`Move ${item.label} down`} onClick={onMoveDown}></button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

For range sliders, use native <input type="range"> wherever possible. It supports arrow key adjustment out of the box. Custom slider implementations frequently break keyboard support:

<input
  type="range"
  min={0}
  max={100}
  value={value}
  onChange={(e) => setValue(Number(e.target.value))}
  aria-label="Price range maximum"
/>
Enter fullscreen mode Exit fullscreen mode

For map or canvas drag interactions, provide explicit pan controls: arrow-key panning and clickable pan buttons in the UI.


5. Target Size (Minimum) -- 2.5.8 -- Level AA

What: The size of the pointer target for interactive elements must be at least 24x24 CSS pixels. Exceptions apply when: the target's offset from adjacent targets is at least 24px, the target is inline within text content, the browser controls the target size (default form controls), or a small size is essential to the information conveyed.

Why: Small tap targets fail users with tremors, limited dexterity, or motor disabilities who use alternative pointer devices with reduced precision. Tightly packed icon buttons, small checkboxes, link-dense navigation menus, and close buttons in notification toasts are the most common failure patterns. Note that 24x24px is the AA minimum. The AAA version (2.5.5, carried forward from 2.1) requires 44x44px. Most mobile UX guidelines already recommend 44px. WCAG 2.2 establishes the legal floor.

How to implement:

Set a baseline minimum for all interactive elements:

button,
a,
[role="button"],
input[type="checkbox"],
input[type="radio"] {
  min-width: 24px;
  min-height: 24px;
}

/* Prefer the AAA-level 44x44 on touch interfaces */
@media (pointer: coarse) {
  button,
  a,
  [role="button"] {
    min-width: 44px;
    min-height: 44px;
  }
}
Enter fullscreen mode Exit fullscreen mode

For icon-only buttons where the visual size is constrained by design, expand the hit area using padding while keeping the visible footprint the same:

.icon-button {
  padding: 10px; /* expands hit area to 44x44 if icon is 24x24 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
Enter fullscreen mode Exit fullscreen mode

The spacing exception is a legitimate tool for constrained layouts. If two 16x16 icons are spaced so their center-to-center distance is 24px or more, they satisfy the minimum even without being 24x24 in physical size. Use it as a fallback, not a design default.


6. Consistent Help -- 3.2.6 -- Level A

What: If a web page provides a help mechanism (human contact, self-help documentation, automated contact, or a contact form), that mechanism must appear in the same relative location across all pages within the site.

Why: Users with cognitive disabilities often need help completing tasks and struggle when support resources appear in different places on different pages. If the help icon is in the top-right corner on the homepage but shifts to the footer on the checkout page, the inconsistency creates friction precisely when the user is most likely to need assistance. This criterion does not require that you have a help mechanism. It only requires that if you do, its location is stable.

How to implement:

This is primarily a layout and design system decision. Anchor help mechanisms inside a shared layout component so they cannot drift between pages:

// Layout.jsx
export function Layout({ children }) {
  return (
    <>
      <GlobalHeader />     {/* help trigger lives here, always */}
      <main>{children}</main>
      <GlobalFooter />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Avoid conditionally hiding the help trigger on specific page types. If suppression is unavoidable (full-screen checkout flows, immersive experiences), make sure the mechanism reappears in the same location once normal layout resumes.

"Same relative location" means the same area of the page (top-right, bottom-right, etc.), not exact pixel coordinates. Responsive layouts that shift the help button between breakpoints are acceptable as long as it is consistently placed within each breakpoint's layout pattern.


7. Redundant Entry -- 3.3.7 -- Level A

What: Information that a user has already provided in a multi-step process must either be auto-populated in subsequent steps or be selectable from previously entered values. Users must not be required to re-enter the same information within the same session unless re-entry is essential (e.g., password confirmation for security) or the information is no longer valid.

Why: Re-entering data is a significant cognitive and motor burden. For users with cognitive disabilities, being asked to retype a name or address they entered three steps ago interrupts task flow, increases error likelihood, and often causes abandonment. For users with motor disabilities, every additional keystroke carries a real physical cost. This criterion formalizes what good UX already recommends: do not ask for something you already have.

How to implement:

In a multi-step React form, store session state at a high level and pre-populate later steps:

// FormContext.jsx
const FormContext = React.createContext({});

export function FormProvider({ children }) {
  const [formData, setFormData] = React.useState({});

  const updateFormData = (values) => {
    setFormData((prev) => ({ ...prev, ...values }));
  };

  return (
    <FormContext.Provider value={{ formData, updateFormData }}>
      {children}
    </FormContext.Provider>
  );
}

// Step 3: Shipping -- pre-populate from billing when same
function ShippingStep() {
  const { formData, updateFormData } = React.useContext(FormContext);
  const [sameAsBilling, setSameAsBilling] = React.useState(false);

  const address = sameAsBilling
    ? formData.billingAddress
    : formData.shippingAddress;

  return (
    <>
      <label>
        <input
          type="checkbox"
          checked={sameAsBilling}
          onChange={(e) => setSameAsBilling(e.target.checked)}
        />
        Same as billing address
      </label>
      <AddressFields defaultValues={address} onChange={...} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

The "same as billing" pattern already present in most e-commerce checkouts is a textbook 3.3.7 implementation. Apply the same logic to any multi-step flow where information asked in step N could have been collected in step N-1 or earlier.


8. Accessible Authentication (Minimum) -- 3.3.8 -- Level AA

What: A cognitive function test (memorizing a password, solving a puzzle, transcribing characters) must not be required at any step of an authentication process unless: an alternative authentication method is available that does not require a cognitive function test, a mechanism is available to help complete the test (such as copy-paste support or a password manager), or the test involves recognizing objects or personal content the user themselves provided.

Why: Password recall is itself a cognitive function test. Many users with cognitive disabilities, memory impairments, or learning disabilities cannot reliably memorize and recall complex passwords on demand. CAPTCHAs add another cognitive or visual puzzle on top of that. This criterion protects access to the authentication layer itself, which is a prerequisite for using everything else on the platform.

How to implement:

The highest-impact single change: allow paste into password fields and respect autocomplete attributes. Blocking paste breaks password managers and forces manual re-entry.

// Wrong: blocks paste, breaks password managers
<input
  type="password"
  onPaste={(e) => e.preventDefault()}
/>

// Right: paste allowed, autocomplete declared
<input
  type="password"
  autoComplete="current-password"
/>
Enter fullscreen mode Exit fullscreen mode

Use the correct autocomplete values for the browser and password managers to fill credentials automatically:

<input type="email" autocomplete="username" />
<input type="password" autocomplete="current-password" />
<input type="password" autocomplete="new-password" /> <!-- registration -->
Enter fullscreen mode Exit fullscreen mode

Additional paths to compliance:

  • Offer magic link login (no password to recall).
  • Support passkeys as an alternative.
  • If you use a CAPTCHA, provide an audio alternative and a non-CAPTCHA path for users who cannot complete visual challenges.

The object recognition exception covers CAPTCHAs that ask users to identify images they uploaded themselves (security images, personal photos). These are permitted because the cognitive anchor is personal memory, not abstract recall.


9. Accessible Authentication (Enhanced) -- 3.3.9 -- Level AAA

What: Same as 3.3.8, but removes the object recognition and personal content exceptions. No cognitive function test of any kind is permitted anywhere in the authentication flow.

Why: Even object recognition and personal image selection require a level of memory and visual processing that users with severe cognitive or visual disabilities may not be able to reliably perform. The AAA version is an absolute requirement: authentication cannot depend on cognitive tests at all.

How to implement:

The only conformant paths at AAA are authentication methods that require no cognitive recall:

Passkeys (WebAuthn/FIDO2): device-level biometric or PIN-based auth. No password to memorize or recall.

// Passkey authentication
const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: serverGeneratedChallenge,
    allowCredentials: [{ type: 'public-key', id: existingCredentialId }],
    userVerification: 'preferred',
  },
});
// Send assertion to server for verification
Enter fullscreen mode Exit fullscreen mode

Magic links: one-time login URLs delivered to a verified email or phone. The user clicks a link in their inbox. No password involved.

SSO delegation: the authentication burden is delegated to a trusted identity provider. The provider's own authentication is outside your conformance boundary.

AAA is not required for most products, but passkeys are rapidly becoming the industry default regardless of compliance requirements. Implementing them satisfies the accessibility requirement and the general trend toward passwordless authentication simultaneously.


Audit Checklist for WCAG 2.2 Compliance

If you are auditing an existing product, prioritize in this order:

Level A (minimum baseline)

  • [ ] Help mechanisms appear in a consistent location across all pages (3.2.6)
  • [ ] Multi-step forms do not re-ask for information already collected in the session (3.3.7)

Level AA (legal and enterprise standard)

  • [ ] No focused element is entirely hidden by sticky headers, footers, or overlays (2.4.11)
  • [ ] All interactive targets are at least 24x24 CSS pixels or have adequate spacing (2.5.8)
  • [ ] Every drag interaction has a single-pointer alternative (2.5.7)
  • [ ] Password fields allow paste and declare correct autocomplete attributes (3.3.8)
  • [ ] No authentication step requires a cognitive function test without an accessible alternative (3.3.8)

Level AAA (aspirational or contractual)

  • [ ] No focused element is partially obscured by author-created overlays (2.4.12)
  • [ ] Focus indicators meet minimum size and 3:1 contrast requirements (2.4.13)
  • [ ] Authentication requires no cognitive function tests of any kind (3.3.9)

The Bigger Picture

WCAG 2.2's additions are tightly scoped around three user groups: people with cognitive or learning disabilities, users with low vision, and users on mobile and touch devices. Every new criterion maps to a failure mode that real products ship regularly: password fields that block paste, drag interactions with no keyboard fallback, sticky headers that swallow focused elements, icon buttons too small to tap precisely.

None of these fixes are expensive once you know what to look for. The authentication changes are often one autocomplete attribute away. The target size and focus visibility issues are a few lines of CSS. The redundant entry problem is a state management question you have probably already partially solved elsewhere in your codebase.

The investment is low. The user impact is not.


Questions about implementing any of these criteria? Drop them in the comments.

Top comments (0)