Your accessibility scanner is probably measuring the cookie banner

We built an accessibility checker. It told us a major Danish retailer was clean. It was auditing their consent overlay. Here are both bugs, with tests you can run yourself in about a minute.

This is a post about two defects in our own tool. We are writing it because both are structural, both are easy to have, and if you run automated accessibility testing against European sites there is a good chance you have at least one of them right now.

Bug one: the consent wall is the page

On 2026-07-31 our checker reported matas.dk, one of Denmark's largest retailers, with four issues and the headline "No keyboard blockers found".

That was not a clean site. That was a clean cookie banner. Underneath it:

title:   Matas.dk
body:    "Vi gor din oplevelse personlig! Kontroller dine data..."
overlay: DIV#coiOverlay   z-index 2147483647
clickable divs on the page: 1137

An overlay at z-index: 2147483647 covering the viewport is not an edge case in Europe, it is the default. GDPR means essentially every commercial EU site puts one in front of the content. If your tooling loads a URL and measures what it finds, it is measuring the banner, and the banner is usually small, simple and reasonably well built. So you get a short report and a false sense of safety.

Reproduce it: load any EU retailer headless, wait two seconds, and print document.body.innerText.slice(0, 120). If it starts with cookie copy, every number you collect after that describes the banner.

Bug two: you cannot see modern click handlers from inside the page

Our headline claim is that we find controls that look clickable but cannot be reached with a keyboard: a div with a click handler and cursor: pointer, absent from the tab order. Rule engines do not report these, and that is not a flaw in them, because in the accessibility tree such an element is just a div with some text.

Our test for "has a click handler" was this:

if (!el.onclick && !el.getAttribute('onclick')) continue;

That only sees inline handlers. React, Vue, Svelte and Angular all bind with addEventListener, which sets no property and no attribute. On essentially every modern storefront, the check was structurally blind.

Reproduce it. Two visually identical, equally unreachable buttons:

<div class="btn" onclick="buy()">Add to cart</div>
<div class="btn" id="modern">Add to cart</div>
<script>
  document.getElementById('modern')
    .addEventListener('click', buy);
</script>

Any detector built on onclick finds the first and misses the second. Ours did exactly that.

The fix

There is no way to enumerate addEventListener handlers from page JavaScript. It has to come from the browser itself, over the Chrome DevTools Protocol:

const cdp = await page.context().newCDPSession(page);
await cdp.send('Runtime.enable');
const { listeners } = await cdp.send(
  'DOMDebugger.getEventListeners', { objectId });
// listeners[].type === 'click'

One refinement worth having: skip elements that also bind keydown or keypress. The author already thought about keyboards, and flagging those is noise dressed up as rigour.

What changed once both were fixed

Same engine, same site, same day. bilka.dk:

 BeforeAfter
Total issuesa handful73
Keyboard-inoperable controls02

Both of the keyboard findings came from the new listener detection, and neither was visible to the old code:

[listener] span.right-menu__link  "Onskeliste"
[listener] a.scroll-to-top        "Tilbage til toppen"

A wishlist link and a back-to-top control that a mouse user can click and a keyboard user cannot reach. Small, ordinary, and completely invisible to the tooling that was supposed to catch them.

Where we still are not finished

We dismiss the consent wall before measuring, preferring "reject" or "accept selected" over "accept all", because consenting to tracking on a site owner's behalf is not ours to do. It does not work reliably yet. On the run that produced the numbers above, the wall was detected on two of six shops and dismissed on neither.

One reason we found and fixed: matas.dk labels its button "Accepter alle" with an acute accent on the second e, and we were comparing raw strings. Danish, Swedish, German and French consent copy is full of diacritics. Matching now normalises them. There are clearly more reasons, and we have not found them.

We are saying so because a tool that quietly measures the wrong thing is worse than no tool, and that is precisely what ours was doing yesterday. Our reports now carry a consentWall field, so you can tell whether you are reading your shop or your cookie banner.

The honest limit

Automated testing finds roughly a quarter to a half of WCAG issues. It cannot tell you whether you comply with anything, and no tool can. The rest needs manual keyboard testing, a screen reader, and disabled people. Anyone selling you a compliance percentage is selling you a number, not an outcome.

We also publish the cases where we find nothing, because a checker that always finds something is selling work rather than measuring it: gov.uk 0, borger.dk 0, figma.com 0.


Try it on your own site. Free, no account, one page per call: nexaaly.com/check, or as an MCP server for coding agents at nexaaly.com/mcp. The engine is MIT on GitHub if you would rather read it than trust it.

Nexaaly, CVR 40643419. access@nexaaly.com