The button element: HeydonWorks

The button element

Some HTML elements, like some places, songs, and smells, have a disproportionate significance for some people. For example, among web accessibility practitioners, any mention of the <button> element will recall a multitude of painful failures and cherished triumphs. Such is the element’s power to make or break the fundamental accessibility of a web interface.

The button element is undoubtedly the most misunderstood and least utilized of all HTML. As a sometime accessibility consultant myself, it’s no exaggeration that I’ve encountered fewer buttons that are <button>s than are other, incorrect elements. That is, ones not helpfully called “button”.

Why on Satan’s puke-green Earth would this be the case?

In the early 2000s, JavaScript accounted for relatively little functionality in web pages. For the most part, interactivity was shared between hyperlinks and forms. Hyperlinks transported you between pages and forms enabled you to submit data to servers.

To change the state of a web page, you might submit a form telling the server to re-render that page according to new data. And that’s the only time anyone ever saw a <button> element: as a submit button.

<button type="submit">Submit</button>

Then, quite suddenly, we all became obsessed with these things called light boxes. In the outernet, a light box is a physical box with a backlit glass pane; a tool for inspecting photographic negatives. On the internet, the term was appropriated to mean a dynamic gallery of digital image files (and consequently had less hipster appeal). A light box would offer thumbnails (smaller images) which would unveil larger corresponding images when clicked. This interaction was made possible using a modest amount of JavaScript.

Nobody used more than a modest amount of JavaScript, back then, because JavaScript couldn’t be trusted to perform more than the occasional parlor trick. Links and forms were still our meat and potatoes. Accordingly, light boxes were built on the principle of progressive enhancement. Should the JavaScript fail, the same content would be accessible by other, albeit more prosaic, means.

Light box thumbnails would be wrapped in anchor elements, pointing to the larger, corresponding images files. Where JavaScript was not available or had errored, clicking a thumbnail would navigate you to the image itself, loading that resource in place of the light box. To revisit the light box page, you just had to press the browser back button.

<a href="/images/ferret-eating-a-twix--large.jpeg">
  <img class="thumbnail" alt="A ferret eating a Twix (large)">
</a>

In the “enhanced” JavaScript version, a script would commandeer the thumbnail hyperlinks to show and hide the larger images in situ. I use scare quotes for “enhanced” quite deliberately, since even this modest use of JavaScript arguably amounted to a degradation rather than enhancement of the experience. Here are just two of many issues, off the top of my head:

  1. To blind users expecting the link to behave as standard, it would seem to be broken. It doesn’t take them anywhere.
  2. To any users, it becomes difficult to access the larger image’s URL for sharing.

To this day, we are convinced people prefer the spectacle of what JavaScript can do over the ability to fully access the content JavaScript is used to gaudishly accessorize. We have no evidence this is the case and we refuse to look for any. We don’t want good reasons to stop dicking around with our favorite web technology.

These thumbnail hyperlinks, when controlled by JavaScript, acted as buttons. They were only coded as <a> elements because we needed hrefs to achieve the “unenhanced” light box experience. Were we confident JavaScript would successfully run, in all cases, the <button> element would have been more apt. Because the “enhanced” behavior is of a button; not an anchor.

In 2024, we are now—extremely and inexplicably—confident everyone has JavaScript up and running, all the time, and without fail. So much so that, when JavaScript does inevitably s**t its pants, all we have to offer is a blank white page. Your average professional developer has either never heard of the term “progressive enhancement” or is quick to condescend any old-timer still intent on applying it.

So you’d expect <button>s to be the element of the era. And yet <a> supplants <button> more often than not. These faux link-buttons are everywhere. It’s a vestige of progressive enhancement that’s outlived any purposeful application and its ubiquity may represent technology’s most prolific cargo cult.

Sometimes you’ll encounter a faux button with an href of javascript:void(0); an explicit negation of standard link behavior. A nullified href is better than no href at all, because it ensures the element is focusable by keyboard. But a JavaScript void, is what you get. I can’t think of a more evocative label than the code itself. Because hyperlinks are what make the web a web and, without them, what do you have?

Anyway. A few practicalities:

  1. If you are progressively enhancing links (properly!) it’s important their semantics correspond to their enhanced behavior. That is, links which do not act as links but as buttons must take the ARIA attribution role="button". Depending on the purpose of the button, you will often have to apply additional state-related attribution such as aria-pressed or aria-expanded.
  2. Since a true <button> has other characteristics to be emulated (such as activation being possible via the Space key), it may be more expedient to replace the <a> markup with standard <button> markup during enhancement. When using custom elements, a sub-tree consisting of links may be replaced with one of <button>s, depending on the element’s purpose. The CSS :defined pseudo-class may be helpful, since it pertains to custom elements that have been defined/initialized (JavaScript has run successfully).
  3. Screen reader users need confirmation/feedback that their actions have been successfully carried out. With links, this is automatic: following a link to a new page will announce that page’s <title> and other introductory information. But button behaviors are often silent. Should you be coding a light box-like component, satisfying WCAG’s 4.1.3 Status Messages may mean implementing a message that confirms when the large image has been replaced. Or it may be enough just to toggle the thumbnail button from aria-pressed="false" to aria-pressed="true". That’s assuming all your images already have alt text, the light box region/landmark is labelled “Image Gallery” and your thumbnails “Thumbnails” or similar.
  4. Progressive enhancement is not a switch between no JavaScript and all JavaScript. Even highly JavaScript-dependent applications can benefit from depending less on JavaScript. You are doing a disservice to any content you render with JavaScript that does not need JavaScript to be consumed. If turning off JavaScript makes your headings, paragraphs, and hyperlinks disappear, go and stand in the corner.
  5. I haven’t got to <div>s yet, so I’ll hold fire. Suffice it to say that <div>s are even less like <button>s than <a>s. Do not put JavaScript click events on <div>s. Ever.

Not everyone is a fan of my writing. But if you found this article at all entertaining or edifying, I do accept tips. I also have a clothing line: