( Operable / WCAG 2.1.1 )
Interactive element is not reachable by keyboard
What is this issue?
An element has a click handler (onclick in HTML, or an equivalent event binding in JavaScript) but no tabindex attribute and no keyboard event handler, on an element type the browser doesn’t make focusable by default, such as <div>, <span>, or <li>. Tab and Shift+Tab move focus past the element without ever landing on it, because nothing in the DOM tells the browser it can receive focus.
This is different from an element that’s focusable but has the wrong role, and different from a focus trap, where focus does reach a component but can’t leave it. Here, focus never arrives in the first place: the element is invisible to the keyboard, even though it’s fully visible on screen and fully clickable with a mouse.
Why does this matter?
Semantic HTML, meaning HTML elements chosen for what they mean and not just how they look (a <button> instead of a <div> styled to look clickable), comes with built-in accessibility behavior for free: keyboard focusability, a role, and Enter/Space activation. Skipping semantic elements in favor of a styled <div> with a click handler throws all of that away, and it has to be rebuilt by hand or the functionality simply doesn’t exist for keyboard users.
This is one of the more severe defects in the keyboard-accessibility cluster because there’s no partial version of it: the user either reaches the control or they don’t. A “Delete item” button built as a <div onclick> with no keyboard wiring isn’t harder to use for keyboard-only users, it’s entirely absent from their experience of the page, with nothing in the Tab sequence even hinting it exists.
Who is affected?
- Keyboard users cannot Tab to the element at all, so any action it triggers is completely unavailable to them, not merely awkward to reach.
- Motor impairments: anyone using a switch device, head pointer, or keyboard-only navigation because a standard mouse isn’t practical for them hits the identical dead end, since these input methods rely on the same Tab-driven focus order.
What users experience
Rosa has cerebral palsy and navigates entirely by keyboard using a mechanical switch that simulates Tab and Enter. On an e-commerce site’s cart page, “Remove item” is built as a styled <div> with an onclick handler and no keyboard support. She tabs through the cart looking for a way to remove an item she no longer wants, and focus jumps straight from the quantity field to the checkout button; the remove control simply isn’t part of her keyboard-driven Tab sequence. She has no way to complete the task and abandons the cart entirely.
How do I fix it?
Replace the non-semantic element with a native <button> (for actions) or <a href> (for navigation) wherever the interaction allows it. This works because native interactive elements are focusable, keyboard-operable, and correctly announced by assistive technology automatically, with no extra code required and no edge case to accidentally miss.
When a native element genuinely can’t be used (a complex custom widget with no direct HTML equivalent), add tabindex="0" to bring the element into the natural Tab order, an appropriate ARIA role (role="button", for example) so assistive technology announces what it is, and a keydown handler that activates the element on both Enter and Space, matching how a native button behaves. All three pieces are required together; any one alone leaves the element partially broken for keyboard users.
Code Examples
<div class="card" onclick="openCard()">
View details
</div><!-- Method 1: native button (preferred whenever possible) -->
<button class="card" onclick="openCard()">
View details
</button>
<!-- Method 2: custom element, fully wired for keyboard -->
<div
class="card"
role="button"
tabindex="0"
onclick="openCard()"
onkeydown="if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); openCard(); }"
>
View details
</div>Method 1 removes the problem at its source: a <button> is focusable, keyboard-operable, and exposes the button role to assistive technology with no additional code. Method 2 rebuilds the same three behaviors by hand for cases where a <div> is unavoidable: tabindex="0" adds it to the Tab order, role="button" announces what it is, and the keydown check makes Enter and Space activate it the same way they would a native button.
Common Mistakes
Mistake: “The element is styled to look like a button, so keyboard users will understand it works like one.” Visual styling communicates nothing to the browser’s focus system. A <div> styled with rounded corners and a drop shadow to look exactly like a button is still, to the DOM, an inert block of text with no built-in keyboard behavior: the browser only grants focusability and key handling to elements that are actually interactive by specification, not ones that merely look the part.
Mistake: “Adding tabindex=‘0’ is the whole fix.” tabindex="0" solves reachability (the element joins the Tab order) but does nothing about activation. Without a keydown handler for Enter and Space, a keyboard user can Tab to the element and then discover pressing the two keys that activate every other button on the page does nothing at all, which is a different but equally blocking failure.
Mistake: “This only matters for buttons, not for whole clickable cards or rows.” Any element with a click handler that triggers real functionality is subject to this rule regardless of what HTML tag it uses: a clickable table row that opens a detail page, a clickable card in a grid, or a custom dropdown option all fail the same way a clickable <div> button does if none of them are keyboard-reachable.
How RedFlag Detects This
Guidance only: RedFlag documents this issue but does not currently flag it; verify manually. This rule ID isn’t wired into RedFlag’s automated detector, manual-check workflow, or coverage matrix at all; it exists purely as documentation today. Reliably distinguishing a decorative, non-interactive <div> from one secretly carrying the only path to real functionality requires understanding what its click handler does, which a static DOM scan can’t judge with confidence.
False negative: every instance of this defect passes every RedFlag scan silently today, since there is no detection logic checking for it at all. False positive: not applicable, since nothing is ever automatically flagged. Manual step: Tab through every page and note any element you can click with a mouse that Tab never lands on. That gap is this rule’s signature, and it has to be found by a human walking the page.
Manual Testing
- Open the page in Chrome or Firefox using only the keyboard; unplug the mouse or resist using it.
- Identify every element on the page that responds to a mouse click: buttons, links, cards, rows, custom controls.
- Press Tab repeatedly from the top of the page and track which of those clickable elements focus actually lands on.
- For any clickable element Tab skips over entirely, confirm it has no
tabindexand no keyboard handler in the DOM; that confirms the failure rather than a coincidental focus-order issue elsewhere. - For elements that do receive focus, press Enter and Space and confirm both activate the element the same way a mouse click would.
Related WCAG Success Criteria
2.1.1 Keyboard: All functionality must be operable through a keyboard interface, without requiring specific timings for individual keystrokes. An unreachable clickable element fails this directly: the functionality behind it exists, but no keyboard-only path reaches it at all.
4.1.2 Name, Role, Value: Every interface component must expose a name, role, and value to assistive technology. A <div> retrofitted with tabindex and a keydown handler but no role="button" becomes reachable without this criterion also being satisfied; reachability and correct semantics are two separate requirements this rule touches on together.
Related Issues
Focus is trapped inside a component is the mirror-image failure despite the similarly named rule ID; that page covers focus getting into a component and being unable to leave, while this page covers focus never arriving at an element in the first place.
Focus skipped an interactive element covers the automated-detectable slice of this same problem: a native or previously-focusable element with a technical cause (like a stray tabindex="-1") axe-core can catch, versus this page’s custom-component case that requires manual review.
Element has a positive tabindex is a related but opposite tabindex misuse: instead of an element missing tabindex entirely, it covers an element whose tabindex value actively disrupts the tab sequence for everyone else.
Content inside an iframe is not keyboard focusable and Scrollable region is not keyboard focusable apply the same “reachable by keyboard” principle to iframes and scroll containers instead of custom clickable elements.
References
Frequently asked questions
Why is this called keyboard-trap if the element is unreachable, not stuck?
The rule ID predates this page's current title and is a naming leftover, not a technical description. The defect this page documents is an element Tab skips over entirely because it was never made focusable: the opposite problem from a trap, where focus gets in but cannot get back out. See Focus is trapped inside a component for that separate issue.
Does adding tabindex="0" alone fix this?
No. tabindex="0" makes the element reachable by Tab, but a div or span still has no built-in keyboard event handling, so pressing Enter or Space on it does nothing. You also need a keydown handler that activates the element on Enter and Space, and usually an appropriate role such as role="button" so assistive technology announces what it is.
Is a native <button> always better than a div with tabindex and a role?
Yes, whenever the interaction is a click-triggered action. A native button gets keyboard focusability, Enter/Space activation, and the button role built in, with no JavaScript required and no risk of missing an edge case a hand-rolled keydown handler might miss, such as held-key repeat behavior.
Does RedFlag catch this automatically?
Not today. Reliably telling a decorative div from a div that is secretly the only way to trigger an action requires understanding what the onclick handler does, which a static DOM scan cannot judge. Use the Manual Testing steps on this page instead of relying on an automated scan result.
Does this apply to elements hidden with CSS as well as div/span click handlers?
It can, if the CSS hides an element from sighted users while an onclick handler still makes it the only way to reach some functionality, but that is a distinct failure pattern from a plain unreachable div. This page is about elements that are visible but never wired for the keyboard in the first place.