What You Get With a Real HTML Button
A clickable <div> is not a lightweight button. It is the beginning of a button implementation.
<div class="button" onclick="save()">Save</div>
To make that control behave properly, you need to add focus, keyboard activation, semantics, disabled behaviour, and state handling. HTML already bundles those details into one element:
<button type="button">Save</button>
Keyboard behaviour comes with the element
A native button participates in the tab order and responds to the expected activation keys. A generic element needs code along these lines:
div.addEventListener("keydown", event => {
if (event.key === "Enter" || event.key === " ") {
doSomething();
}
});
That snippet is still incomplete. Space-key handling, repeated events, disabled state, focus styling, and pointer behaviour can introduce more details. Reimplementing browser controls is an excellent way to learn how many details browser controls contain.
Forms give buttons another job
<form>
<input name="email" type="email">
<button type="submit">Subscribe</button>
</form>
This submits without a JavaScript click listener. For a control that should not submit, say so:
<button type="button">Open help</button>
type="reset" also exists, but I rarely use it. A control whose main feature is erasing the form is surprisingly easy to press.
Buttons can be disabled natively:
<button type="button" disabled>Save</button>
Be careful not to leave users with no explanation of why an important action is unavailable.
Native semantics do not dictate the design
.button {
border: 0;
border-radius: 0.5rem;
padding: 0.75rem 1rem;
background: black;
color: white;
font: inherit;
cursor: pointer;
}
<button class="button" type="button">Save</button>
Keep a visible focus style:
button:focus-visible {
outline: 3px solid currentColor;
outline-offset: 3px;
}
Removing the browser outline without a replacement makes the design cleaner mainly for people using a mouse.
Icon-only controls need a name
<button type="button" aria-label="Close">
<svg aria-hidden="true">...</svg>
</button>
Visible text is better when it fits. When it does not, the accessible name should describe the action rather than the icon’s shape.
Use a link when activation navigates to a URL:
<a href="/contact/">Contact</a>
For an action, start with <button>. The native element is not a limitation to work around; it is a pile of solved problems you get to keep.