A div with onClick is not a button
The Pay button looks fine and works with a mouse. A customer who uses the keyboard presses Tab, and focus jumps straight past it. A screen reader reads the label as plain text. Neither of them can pay, and neither of them files a bug report. They leave.
Why it happens
A <button> gets three things from the browser for free: it is in the Tab order, Enter and Space press it, and assistive technology announces it as a button. A <div> or <span> gets none of them. Adding onClick only handles the mouse.
<div className="pay-button" onClick={handlePay}>
Pay now
</div>The cost is real. People who cannot use a mouse, people with a broken trackpad and power users who move by keyboard all hit the same wall. In many markets, including the US and the EU under the European Accessibility Act, an online checkout that keyboard users cannot complete is also a legal risk.
The fix: use a real button
Swap the element. Set type="button" so it does not submit a surrounding form by accident, and reset the default styles if they get in the way. Keep a visible focus style so keyboard users can see where they are.
<button type="button" className="pay-button" onClick={handlePay}>
Pay now
</button>
/* CSS */
.pay-button { all: unset; cursor: pointer; /* your styles */ }
.pay-button:focus-visible { outline: 2px solid #34d399; outline-offset: 2px; }If the click goes to another page, use a link (<a href> or your framework's Link) instead. Links also work with the keyboard, open in a new tab and show the address on hover.
When you cannot change the element
Sometimes a library renders the wrapper for you. Then add what the browser would have given a button: a role, a place in the Tab order and a key handler for Enter and Space.
<div
role="button"
tabIndex={0}
onClick={handlePay}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault(); // stop Space from scrolling the page
handlePay();
}
}}
>
Pay now
</div>Verify it
jsx-a11y ESLint rules click-events-have-key-events and no-static-element-interactions catch the same pattern, and an automated check such as Lighthouse or axe reports missing roles.How Zelsis catches this
Zelsis flags <div> and <span> elements with onClick and no role, tabIndex or key handler, and suggests the <button> fix on the pull request.
- UI-A11Y-02Clickable div or span without role, tabIndex or keyboard handler
Check your own repo
Public repositories scan free. Paste a GitHub URL on the dashboard and every finding comes with the file, the line and a fix.
Scan a repository free