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:
| Before | After | |
|---|---|---|
| Total issues | a handful | 73 |
| Keyboard-inoperable controls | 0 | 2 |
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