- ** Daily Tips **
- October 31, 2025
One of the weirdest “debates” I seem to perpetually have with framework-enthusiastic developers is whether or not a <div> is “just as good” as a <button>.
Spoiler: it’s not. Let’s dig in.
The problem
Among the React crowd, and also among people who seem to enjoy HTMX, I see a lot this…
<div onclick="showSignIn()">
Open Modal
</div>
function showSignIn () {
// Code to show the sign-in modal.
// The details of what happens here vary by stack.
}
What’s wrong with this?
- This element does not announce itself as an interactive element to screen reader users.
- You can’t focus on a
<div>with a keyboard. - The event only fires on
click, not when theEnteror `Space Bar…
- ** Daily Tips **
- October 31, 2025
One of the weirdest “debates” I seem to perpetually have with framework-enthusiastic developers is whether or not a <div> is “just as good” as a <button>.
Spoiler: it’s not. Let’s dig in.
The problem
Among the React crowd, and also among people who seem to enjoy HTMX, I see a lot this…
<div onclick="showSignIn()">
Open Modal
</div>
function showSignIn () {
// Code to show the sign-in modal.
// The details of what happens here vary by stack.
}
What’s wrong with this?
- This element does not announce itself as an interactive element to screen reader users.
- You can’t focus on a
<div>with a keyboard. - The event only fires on
click, not when theEnterorSpace Barkeys are pressed (again, keyboard users).
I’ve seen this in a lot of code bases. I’ve seen it in a lot of demos.
I’ve had arguments with a very prominent React thought leader whose name starts with R who insisted that using a <div> was “more accessible” than using a <button>, and that Twitter made the right decision in using this pattern in their app.

It’s wrong. It’s all wrong.
The “fixes” aren’t
Many HTML elements have implicit roles that tell assistive tech like screen readers what they do.
The <button> element is one of them. It has an implicit [role] of button, which tells screen reader users it can be interacted with and will trigger some type of behavior in the app.
The HTML [role] attribute can be used to add or modify the role of an element. And so, folks like React Ry–thought-leader-guy will say stuff like (I’m paraphrasing)…
That attribute exists for a reason. You can add
[role="button"]to adivto give it the correct semantics.
OK, that addresses one issue.
That role doesn’t affect focusability (or lack thereof) or keyboard behavior. Visually impaired users and people who navigate with a keyboard still can’t use it.
“No worries!” they say. “We can fix that, too!”
You can make the element focusable with the [tabindex] attribute.
<div
onclick="showSignIn()"
tabindex="0"
>
Open Modal
</div>
You shouldn’t, though! Seriously, just don’t fuck with focus order.
It’s way too easy to go down this path and then fuck it up and have folks jumping all over the page instead of navigating through in the normal and expected order.

And again, still no keyboard interactivity.
But don’t fear! You can add that, too. You just need to listen for all keydown events, and then filter them out by event.key so that you only run your code if the Enter or Spacebar keys were pressed (the latter means checking for a literal space: ' ').
That can’t run on the element, either. You’ve got to attach that even to the document and figure out which element has focus.
document.addEventListener('keydown', (event) => {
// Only run on Enter and Spacebar presses
if (event.key !== 'Enter' & event.key !== ' ') return;
// Make sure the element you care about how focus
const notRealBtn = document.activeElement.closest('[onclick]');
if (!notRealBtn) return;
// Run your code, somehow...
});
So um… ok, I guess it is technically a fix, but…
You’ve just recreated all of the functionality a <button> gives you for free
Seriously, WTF would you do that?!?
All of these hoops to write this HTML…
<div
onclick="showSignIn()"
tabindex="0"
>
Open Modal
</div>
When you could write this HTML instead…
<button onclick="showSignIn()">
Open Modal
</button>
A <button>…
- Has the correct
[role]implicitly. - Is automatically focusable.
- Fires a
clickevent in response toEnterandSpacebarpresses when it has focus.
Look, I’m a lazy developer.
And I suspect, if you’re someone who loves tools like React, you probably are, too. It’s cool, I get it! The best code is the code you didn’t write and all that.
So, be even lazier.
Use the correct element for the job, and avoid writing a bunch of extra code!