Element not reachable by keyboard (keyboard:unreachable)

The keyboard:unreachable rule flags something that behaves like a control – it has a click handler, or a role such as button or tab – but that no Tab press ever lands on. A mouse user can use it; a keyboard user never gets there. The fix is almost always to use the real element: a <button> for actions, an <a href> for navigation.

What the rule means

WCAG 2.1.1 requires that everything a page lets you do can be done with a keyboard. Native controls – links with an href, buttons, form fields, <summary> – are in the tab order and respond to Enter or Space without any extra code. A <div> or <span> is neither: it gets no focus, and pressing Enter on it does nothing, however it looks and whatever its click handler does.

A role alone does not change that. role="button" tells assistive technology to announce a button, but it does not put the element in the tab order and does not make Enter or Space work. That is a promise the markup does not keep.

Who is affected

Everyone who operates a page without a mouse: people with motor disabilities using a keyboard, switch or mouth stick, blind people using a screen reader, and people using voice control, which often works through the same focusable elements. When the one control that opens a product, adds to the basket or accepts cookies is unreachable, the task cannot be finished at all.

Why the check fails

  • Clickable cards – a <div onclick> around a teaser or a product, with no link inside.
  • Icon buttons built from <span> or <i> with a click handler: close, menu, search.
  • ARIA widgets half-built: role="tab", role="menuitem" or role="option" without tabindex or keyboard handling.
  • tabindex="-1" on a control that was meant to be reachable, often copied from a pattern that manages focus by script.
  • Cookie banners and chat launchers from third parties that only listen for mouse clicks.

How to fix it

Replace the stand-in with the element that already does the job. You get focus, Enter and Space, and the right role for free.

<!-- Before: a card only a mouse can open -->
<div class="product-card" onclick="location.href = '/products/desk-lamp/'">
  <img src="/img/desk-lamp.jpg" alt="">
  <h2>Desk lamp</h2>
  <p>€49.00</p>
</div>
<!-- After: the heading holds a real link; CSS can stretch it over the card -->
<div class="product-card">
  <img src="/img/desk-lamp.jpg" alt="">
  <h2><a href="/products/desk-lamp/">Desk lamp</a></h2>
  <p>€49.00</p>
</div>

The same for an icon that acts:

<!-- Before: announced as a button, but no Tab reaches it -->
<span class="icon-close" role="button" aria-label="Close" onclick="closePanel()">×</span>
<!-- After: a real button -->
<button type="button" class="icon-close" aria-label="Close" onclick="closePanel()">×</button>

If you really cannot change the element, it needs tabindex="0", a handler for Enter (and Space, for buttons) and the matching role – three things the native element gives you in one.

How to test it manually

  1. Press Tab from the top of the page and note every control that focus visits.
  2. Compare with everything you can click: cards, icons, tabs, accordions, sliders, cookie buttons.
  3. For each one focus skipped, you have found this problem. For each one it visited, press Enter (and Space for buttons) and check it works.
  4. The browser's accessibility tree (developer tools) shows whether an element is focusable and what role it has.

2.1.1 Keyboard, Level A. Related: 4.1.2 Name, Role, Value, because a clickable <div> also has no role, and 2.4.3 Focus Order.

How Reviseberg reports it

This rule comes from Reviseberg's keyboard agent, not from axe-core. After walking the journey you choose with real key presses, the agent looks for elements that promise to be controls – an onclick attribute, or a role such as button, link, checkbox, radio, tab, menuitem, switch or option – that are visible but neither focusable by nature nor given a tabindex of 0 or more. Each one is reported with the message "This div behaves like a control but cannot be reached with the keyboard", its selector and the reason it looked interactive.

What it cannot see: a click handler attached with addEventListener leaves no trace in the page that a script can read, so a <div> wired up that way is not reported. The agent under-reports on purpose rather than guessing from a pointer cursor, which would flag every styled card – so a clean result here is not proof, and the manual test above still matters.

  • undefined – the opposite problem: focus that cannot leave.
  • undefined – a control inside another control.
  • undefined – a scrolling area the keyboard cannot reach.

Run the keyboard agent on your site – free report

Frequently asked questions

Is role="button" enough?

No. The role changes what assistive technology announces, not how the element behaves. It still needs tabindex="0" and handlers for Enter and Space – or, simpler, be a <button>.

Why not just add tabindex="0" to the div?

Focus alone is not operation: Enter and Space still do nothing until you script them, and the element still has no role. A <button> or <a href> does all of it.

Why doesn't the agent find every unreachable control?

A handler added with addEventListener is invisible to any script on the page. The agent reports what it can prove rather than guessing.

Does axe-core find this?

Partly, from another angle: rules such as nested-interactive and scrollable-region-focusable catch related patterns. Whether a clickable element can be reached by pressing Tab needs a keyboard.

Sources

  1. W3C, Understanding SC 2.1.1 Keyboard – https://www.w3.org/WAI/WCAG22/Understanding/keyboard
  2. W3C, ARIA Authoring Practices Guide: Button Pattern – https://www.w3.org/WAI/ARIA/apg/patterns/button/
  3. MDN, ARIA: button role – https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
  4. W3C, Using ARIA: first rule of ARIA use – https://www.w3.org/TR/using-aria/

See what a scan finds on your site

One page in about thirty seconds, no email needed. The full report covers up to 100 pages and a keyboard journey.