slop audit
Blog post 28 / Accessibility and interaction

Cookie banner accessibility: developer checklist

Use this cookie banner accessibility checklist to test dialog semantics, keyboard flow, focus, controls, target size, contrast, and production behavior.

By Slop Audit · 10 min read

Cookie banner accessibility starts with a simple release question: can every visitor understand the choice, reach each control, make a decision, and continue to the page? A banner can look polished and still cover the main task, expose unnamed buttons, lose keyboard focus, or return after the visitor has already chosen.

Use this checklist on the deployed page in a fresh browser session. Test the banner as an interface, then test the consent behavior as a separate product contract. An automated scan can find useful evidence, but it cannot decide whether the consent mechanism meets every legal, analytics, or accessibility requirement for your site.

Cookie banner accessibility checklist

Check the first-visit path in this order:

  1. Open the deployed page without saved cookies or local storage.
  2. Decide whether the banner is modal or non-modal, then implement that behavior consistently.
  3. Give the banner a clear name and use native buttons for each action.
  4. Reach and operate every choice with a keyboard.
  5. Keep focus visible, ordered, and inside a true modal until it closes.
  6. Make the controls large enough, spaced apart, and readable at narrow widths.
  7. Verify that the page is usable after accept, reject, save, and later preference changes.
  8. Check storage and network behavior against the consent requirements separately.
  9. Repeat the test on important templates and after consent-platform updates.

Start with a clean session because a saved decision can hide the banner and make a release test pass without exercising it. Record the URL, viewport, locale, banner version, and choice you tested so another developer can reproduce the result.

Choose modal or non-modal banner behavior

A modal cookie banner prevents interaction with the page until the visitor makes or dismisses a choice. A non-modal banner remains visible while the page behind it stays available. Do not combine the visual style of one pattern with the keyboard behavior of the other.

For a true modal, the W3C modal dialog pattern expects focus to move into the dialog, Tab and Shift+Tab to remain within it, and focus to return to a logical place after it closes. The dialog needs a programmatic name, usually from a visible heading. Background content must be inert while the modal is open.

The HTML dialog element can provide useful browser behavior. Calling `showModal()` displays it in the top layer and makes the rest of the document inert. You still need clear content, working choices, deliberate initial focus, responsive layout, and tested close behavior.

A non-modal banner should not trap focus or mark the page behind it as inert. Put it in a named region, keep it in a logical document position, and let keyboard users move between the banner and page. If it stays fixed to the viewport, confirm that it does not cover the main heading, primary action, or the control that currently has focus.

Only use aria-modal="true" when the interface actually behaves as a modal for keyboard, pointer, and assistive-technology users. ARIA cannot turn an interactive background into an inert one.

Use native controls and clear names

Accept, reject, save, and open-preferences actions are buttons. Use a native button rather than a clickable div, a fake link, or text with a JavaScript handler. Standard controls already expose much of the name, role, and state contract described by WCAG Name, Role, Value.

Write labels that identify the decision:

  • Accept optional cookies
  • Reject optional cookies
  • Manage cookie preferences
  • Save cookie preferences

Avoid several buttons named Allow, Okay, or Continue when the result is unclear. Keep the visible label in the accessible name so speech-input users can activate the words they see. The accessible name guide covers native labels, aria-labelledby, icon buttons, and label conflicts in more detail.

This small modal example gives the dialog a visible name and keeps every choice inside the same form:

<dialog id="cookie-preferences" aria-labelledby="cookie-title">
  <form method="dialog">
    <h2 id="cookie-title">Cookie preferences</h2>
    <p>Choose whether this site may use optional analytics cookies.</p>

    <button value="reject" type="submit">
      Reject optional cookies
    </button>
    <button value="manage" type="button">
      Manage cookie preferences
    </button>
    <button value="accept" type="submit">
      Accept optional cookies
    </button>
  </form>
</dialog>

The markup does not persist a decision or control scripts by itself. Connect the form result to your consent system, then verify the stored choice and resulting network behavior. Do not add a close icon with no name or a dismissal path that silently records consent.

Test cookie banner keyboard accessibility

Run the complete first-visit flow without a pointer:

  1. Reload the page in a clean session.
  2. Confirm that a modal receives focus when it appears.
  3. Press Tab and Shift+Tab through every choice.
  4. Confirm that the visible order matches the decision order.
  5. Activate each action with Enter or Space as appropriate.
  6. Open and close the detailed preference view.
  7. Verify that focus does not move behind a modal.
  8. Confirm that focus returns to a logical visible element after close.
  9. Continue to the page's primary action with the keyboard.

WCAG requires page functionality to be operable through a keyboard interface. A contained Tab loop is expected while a modal dialog is open, but the visitor must have a clear way to complete or leave that dialog. A repeating target in a non-modal banner is a trap, not a focus-management feature.

Use the keyboard accessibility testing checklist for visible focus, reverse traversal, dialogs, and manual tests that a short automated pass cannot prove.

Keep focus visible and out from under the banner

A bottom banner can leave focus on a page control that is now hidden behind it. A tall modal can move its own last action below the viewport. Test at narrow and short viewports, browser zoom, and increased text spacing rather than checking one desktop screenshot.

The WCAG 2.2 guidance for Focus Not Obscured covers content such as sticky headers, footers, and persistent overlays that can hide a focused control. Useful fixes include reducing the fixed surface, allowing the banner content to scroll, reserving page space, and using scroll padding where a persistent header or footer would cover focused content.

Keep the focus indicator distinct on every banner background and button state. Check the default, hover, focus, selected, disabled, and error states. A strong outline on the page may disappear when the same button is rendered on a dark consent panel.

Check target size, contrast, and narrow layouts

Cookie controls often fail at the edge cases: a small close icon, dense purpose toggles, low-contrast secondary text, or an action row that overflows on a phone.

The WCAG 2.2 Target Size (Minimum) guidance describes a 24 by 24 CSS pixel minimum target with defined exceptions and spacing rules. New controls shown inside a modal or cookie banner are still in scope. Treat that threshold as a floor, not a reason to make the primary choices difficult to press.

At each supported narrow width, check that:

  • all choice labels remain visible without horizontal scrolling;
  • buttons do not overlap, clip, or reverse their meaning through layout changes;
  • the preference list has a usable internal scroll path when needed;
  • text, borders, toggles, and focus indicators retain sufficient contrast;
  • the banner does not cover the page's main task after it becomes non-modal; and
  • zoomed text does not push the only reject or save action out of reach.

Use the focused guides for touch target size and color contrast accessibility when a finding needs measurements and CSS examples.

Add a deployed cookie banner test to Playwright

Use a new browser context so saved state cannot bypass the first-visit experience. Role locators make the test depend on the same dialog name and button names exposed to assistive technology:

import { expect, test } from "@playwright/test";

test("reject choice persists in a fresh session", async ({ browser }) => {
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto("https://example.com/");

  const banner = page.getByRole("dialog", { name: "Cookie preferences" });
  const reject = banner.getByRole("button", {
    name: "Reject optional cookies",
  });

  await expect(banner).toBeVisible();
  await reject.focus();
  await expect(reject).toBeFocused();
  await page.keyboard.press("Enter");
  await expect(banner).toBeHidden();

  await page.reload();
  await expect(banner).toBeHidden();

  await context.close();
});

Add cases for accept, reject, manage, save, and change-preferences behavior. If the design uses a non-modal region, locate it by its region name instead of asserting a dialog role. Do not make the test pass by adding an inaccurate role.

Playwright's accessibility testing guide recommends putting the page into the state you want to inspect before running an axe analysis. Scan the initial banner and any preference panel after opening it. Then keep a manual keyboard and screen-reader pass because automated rules do not evaluate the complete interaction.

Verify consent behavior separately

An accessible interface can still save the wrong choice or allow a request that your product requirements say must wait. Test the behavior behind each control independently from the markup review.

For each supported region or policy mode, record:

  • cookies and storage before any choice;
  • requests made before the banner is resolved;
  • the value stored after accept, reject, and custom preferences;
  • requests made after each choice;
  • behavior after reload and on a second page;
  • the path for changing or withdrawing a saved choice; and
  • what happens when storage is unavailable or cleared.

Keep expected vendors and storage keys in a reviewed fixture rather than accepting any request with a familiar analytics label. If a consent platform or tag manager changes, rerun the full matrix. Slop Audit does not validate this legal or tracking contract, so retain dedicated network tests and a policy review.

Check cookie banner accessibility with Slop Audit

Run a Slop Audit on the deployed page in addition to the focused interaction tests. During its bounded Browser Run, Slop Audit renders mobile and desktop viewports when available. Its blocking-overlay condition looks for visible fixed elements that cover at least 30 percent of the viewport and own its center point. It also records evidence for accessible names, form labels, axe violations, text contrast, keyboard focus and possible traps, clipped controls, mobile overflow, and target geometry when those checks complete.

The overlay rule is a launch heuristic. A large banner is not automatically invalid, and a smaller banner can still hide an important control. The scan does not choose a consent option, inspect every preference state, confirm saved consent, classify tracking requests, run a screen reader, provide legal advice, or certify WCAG conformance.

Use the finding to identify the route, viewport, control, or overlay that needs review. Fix the deployed interface, rerun the same URL, and add the stable checks to the website regression testing checklist.

Cookie banner release sign-off

fresh session → modal or region contract → clear control names
→ keyboard path → visible focus → narrow layout → target and contrast checks
→ accept/reject/manage behavior → storage and network checks → deployed scan

The banner is ready when the visitor can understand the choice, complete it with their input method, reach the page afterward, and get the same behavior on the deployed site that the interface promised.