
A complete <dev>
guide for better buttons
Buttons are one of the most simple, yet deeply fundamental and foundational building blocks of the web. Click a button, and you submit a form or change the context within the same page. But there is a lot to know from the HTML implementation and attributes, styling best practices, things to avoid, and the even-more-nuance implementations of the buttons — links
and button-like inputs
.
This article covers, building —
- Links,
- Buttons,
- and Button-like Inputs
We’re going to deep-dive into all three of them, and for each, dig into the 🏗️ HTML implementations, 🖌 CSS and ⚙️ JS considerations, ❤️ Accessibility information, and ‼️ Pitfalls/Bad practices. By covering all that, we’ll have a better UX implementation of each.
Quick rules of thumb on when to use each…
- Are you moving to another page or a different part of the same page? — Use a Link.
- Are you building a JavaScript-only powered clickable element? — Use a button.
- Are you submitting a form? — Use a submit input.

Links
Thumb-rule — if it navigates to other pages on or off a site or different parts of the same page, that’s a link.
🏗️ HTML Implementation
A link can be Absolute (navigates to the different domain) or Relative (to a different page in the same domain)
<!-- Absolute link -->
<a href="https://developer.mozilla.org">Check MDN</a><!-- Relative link -->
<a href="/about">About Codeburst</a>
And links can also be hash based or jump links
<!-- Jump link -->
<a href="#some_part_of_my_page">Scroll to Some Part</a><!-- will jump to... -->
<section id="some_part_of_my_page"></section>
Clicking that link will “jump” (scroll) to the first element in the DOM with an ID that matches.
Absolute and Relative can also have hash in them.
Considerations —
- Don’t use
target=”_blank”
on relative links. Read more on when to use target - Anchor link without href is called a placeholder link and is a valid syntax.
- Anchor links don’t have
disabled
oraria-disabled
attributes. - Don’t use
href="#0"
as a button. Use button instead. - Can use
href="#"
orhref=”#”
to scroll to the top section of the page.
🖌 Styling considerations
- ‼️ Don’t remove focus styles.
- Handle the following states of link
:link
(default state),:visited
,:hover
,:focus
and:active
- You can style a link to look as a button, but need more code to make it work as a button. So, don't !!
- While converting webpages to PDF,
@media print {
a[href*='//']:after {
content:" (" attr(href) ") ";
color: $primary;
}
}// read more here -> https://flaviocopes.com/css-printing/#links
- Nesting
<div></div>
inside<a ...></a>
is valid.
⚙️ JS considerations
- It’s possible to intercept a click on a link and do something with JavaScript instead — Why? Single Page Apps, or doing fancy things before the redirect with
window.location
then usereturn false
orpreventDefault()
- Using
onClick()
orhref="#0"
— use Button instead.
❤️ Accessibility
We covered some accessibility in the sections above (it’s all related!), but here’s some more to think about.
- Have understandable text (“documentation” instead of “click here”)
- Don’t use the URL itself as the text
- Images-as-links — use
alt
on<img .../>
- Making a link recognized as a button? role=”button”. But is it necessary ?
- Add accessKey for keyboard shortcuts
‼️ Pitfalls/Bad practices
- Don’t use
target="_blank"
withoutrel="noreferrer"
andrel="noopener"
. Read this - Dont override cursor properties.

Buttons
If anything, the <button> element is underused. A good rule of thumb is that if a link has “no meaningful href”, it should be a button. Here’s another: if clicking it doesn’t do anything without JavaScript, it should be a button.
Let’s understand a button —
- Any clickable element which can be used in forms or any part of the website to submit data or to change the context of the current view.
- Which has focus, active, hover(optional) and disable states.
- And importantly, which can be triggered by space and enter.
🔭 Observation
Clicking button by default focuses it in all of the Windows OS related browsers, but in Mac OS — it only happens in chrome and opera.
🏗️ HTML Implementation
General and more frequent ways implementing button-
<!--1-->
<button> Checkout </button><!--2-->
<button type="reset"> Reset </button><!--3-->
<button type="submit"> Submit </button>
🔭 Observation
A general
<button></button>
submits the form by default. Specifytype='button'
to override it.
🖌 Styling considerations
Also applies for input-like buttons
Browsers have a unique way of styling elements and tightly depends on the browser-engine of the browser and has led to the concept of Vendor Prefixes. The default style of every browser is called user-agent style of the browser. And buttons are no exception — every browser renders button differently, unless they share the same engine.
- Make a button look uniform using Normalizer, Reboot, Sanitise or using shorthand
all
- Don’t remove the
outline
for a button. - Don’t remove
:focus
:disabled
states.
⚙️ JS considerations
Also applies for input-like buttons
- Handle EventBubbling and RageClicks in the buttons.
- Handle Async state while the button operation is pending.
❤️ Accessibility
TLDR; — Focus on the clicked button must be present/return to it unless it leads to the context change.
As defined by ARIA
- If activating the button opens a dialog, the focus moves inside the dialog.
- If activating the button closes a dialog, focus typically returns to the button that opened the dialog unless the function performed in the dialog context logically leads to a different element.eg., Click on the cancel dialog must return the focus to the model triggering button. If the dialog has the submit button which changes the context, then the focus must set to the appropriate element in the new context.
- If activating the button does not dismiss the current context, then focus typically remains on the button after activation. Eg., Like an Apply or Recalculate button. But not the reset button.
- If the button action indicates a context change, such as a move to the next step in a wizard or adds another search criteria, then it is often appropriate to move focus to the starting point for that action.
- If the button is activated with a shortcut key, the focus usually remains in the context from which the shortcut key was activated. A button in a focused card has a shortcut key assigned and is triggered, the focus should still be on the card rather than a button unless it changes the context.
- Minimum button size required in 44x44 px
- if using ICON only Buttons mention
label
oraria-label
on the button.
🔭 Observation
autocomplete
works only on firefox and persists the dynamic state of the button.
‼️ Pitfalls/Bad practices
- Avoid usage of
div
orspan
as buttons because that might disable the accessibility features that the browser has to offer. You may have to mimic the button behavior withtabindex
, CSS, and JS. And that’s a lot of code for a simple button 😠
Using HTML semantics and letting the browser do most of the work is more future-friendly.
🔭 Observation
autofocusing
a button is considered as a security threat and gets blocked by-default in case of iframe with sandbox enabled.

Button like Inputs
You might think of inputs as places where you type things (<input type=”text”/>
) or click to choose things (<input type=”radio”/>
), but there are a variety of inputs that are essentially submit (or reset) buttons for forms.
🏗️ HTML Implementation
Input button cannot contain children elements, hence there are limitations here. <button></button>
can contain children.
<!--1--
<input type="button" value="A button"/><!--2-->
<input type="submit"/><!--3-->
<input type="reset"/>
🔭 Observation
label
is not required for input-buttons as suggested for otherinput
elements.✋ TIP
If no
disabled
attribute is specified on a button. It inherits the disabled state from the parent element.
⚙️ JS considerations
- Validations are to be handled and
required
is handled by form
❤️ Accessibility
🔭 Observation
“Firefox will, unlike other browsers, by default, persist the dynamic disabled state of a
<button>
across page loads. Use theautocomplete
attribute to control this feature” — as quoted on MDN
References
- https://www.w3.org/TR/wai-aria-practices/#button
- https://marcysutton.com/links-vs-buttons-in-modern-web-applications
- https://webaim.org/standards/wcag/checklist
- https://dequeuniversity.com/rules/axe/3.3
- https://inclusivedesignprinciples.org
- https://mcc.id.au/blog/2013/10/all-unset
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a
Thank you Chris Coyier for helping me on this 🙌
Have something to add? I’d be happy to do it. Post in the comments or tweet me
Cheers, KD 🤘