You can't create a button

One of the most important aspects of accessibility is managing focus and user interaction. By default, all links and form controls can get focus. That allows you to use the tab key to navigate between them and, when one of the elements has focus, activate it by pressing the enter key. This paradigm works amazingly well regardless of the complexity of your web application. As long as a keyboard-only user is able to navigate between links and form controls then it’s possible to navigate the application.

Unfortunately, sometimes web developers try to get a bit too clever in creating their interfaces. What if I want something to look like a link but act like a button? Then you end up seeing a lot of code that looks like this:

<a href="#" onclick="doSomething()">I'm a button</a>

That code should turn your stomach a little bit. It’s a link that goes nowhere and does nothing. All it does is attach an onclick event handler to give it a purpose. Because the desired appearance for this element currently is link-like, the markup uses a link and JavaScript.

Those who are familiar with ARIA may “fix” the problem by using the following:

<a href="#" role="button" onclick="doSomething()">I'm a button</a>

By setting the ARIA role to button, you’re now telling the browser and screen readers that this link should be interpreted as a button (that does an action on the page) rather than a link (that navigates you away). This has the same problem as the previous code except that you’re trying to trick the browser into treating the link as if it were a button. In reality, it would be most appropriate to just use button:

<button onclick="doSomething()">I'm a button</button>

The markup to use should never be based on the appearance of a UI element. Instead, you should try to figure out the real purpose of that element and use the appropriate markup. You can always style button to look like a link or a link to look like a button, but those are purely visual distinctions that don’t change the action.

If these were the worst sins of web applications that I have seen, I would be pretty happy. However, there is another even more disturbing trend that I’m seeing. Some Web applications are actually trying to create their own buttons by mixing and matching different parts of HTML, CSS, and JavaScript. Here’s an example:

<div tabindex="0" role="button" onclick="doSomething()">I'm a button</div>

This is a valiant attempt at creating a button out of a <div>. By setting the tabindex attribute, the developer has assured that keyboard users can navigate to it by using the tab key. The value of 0 adds the elements into the normal tab order so it can receive focus just like any other link or button without affecting the overall tabbing order. The role informs the browser and screen readers that this element should be treated as a button and the onclick describes the behavior of the button.

To anyone using a mouse, assuming the styling is correct, there is no distinction between this element and an actual button. You move the mouse over and click down and an action happens. If you’re using a keyboard, however, there is a subtle but important difference between this and a regular button: almost all browsers will not fire the click event when the element has focus and the enter key is pressed. Internet Explorer, Chrome, Firefox, and Safari all ignore the enter key in this situation (Opera is the only one that fires click).

The enter key fires the click event when used on links and buttons by default. If you try to create your own button, as in the previous example, the enter key has no effect and therefore the user cannot perform that action.

This horrible pattern is found most frequently in Google products. Perhaps the most ironic usage is in Gmail. When you press the ? key, a dialog pops up showing you available keyboard shortcuts and allowing you to enable more advanced shortcuts.

Gmail keyboard shortcuts dialog

It looks like the word “Enable” is a link, so you press tab a few times to give it focus and press enter. Nothing happens. Why? Because the link is actually neither a linkage nor a button, it’s a <span>. Here’s the actual code:

<span id=":s7.pl" role="link" class="aoy" tabindex="1">Enable</span>

Almost exactly the problematic pattern mentioned earlier in this post. So basically in order to turn on keyboard shortcuts you need to be able to use a mouse. In fact, many of the buttons on Gmail are made in this way. If not for the keyboard shortcuts it would basically be unusable without a mouse.

Gmail isn’t the only Google site that uses this pattern. It can be found throughout the network of Google sites, including Google Groups and Google Analytics (which also hides focus rectangles). This alone makes Google products incredibly challenging to use for sighted users who don’t use pointing devices.

If you expect the user to interact with something, then you need to use either a link or button. These have the correct behaviors both in terms of getting focus and activating when the enter key is pressed. Links should be used whenever the action is a navigation (changes the URL) and buttons should be used for all other actions. You can easily styled these to create the visual effect that you want, but nothing can replace the accessibility of the native links and buttons.

Comments

  1. Adam Dougherty

    So do you suggest we only use the button tag when interacting with Javascript? What if we want a click handler but its not necessarily a button? Also what about the crawl-ability of buttons and their values, do we know the differences with Googles take on these?

    Great post, thanks.

  2. Priit Pirita

    Oh, there are too many problems with buttons. Assuming you wan't to style them nicely.
    On FF you need to kill the inner focus, on IE you need hacks to vertically center the text (different hacks for different versions), on Opera you need to kill the "press-down" effect. Too much CSS hackery and generally not worth the trouble.

  3. Nicholas C. Zakas

    @Priit - I think you're making a bigger deal out of this than necessary. I've managed to get nice looking buttons using a reset style sheet and just a few styles.

  4. Nicholas C. Zakas

    @Adam - yes, that's what I'm saying. If you want a click handler then you definitely want a button. What I think you're saying is you might want to click handler that looks like a link, which you can still do by styling the button with CSS.

  5. Pierre Bertet

    What about an additional keypress event?

    My first choice is always a button, but when not possible (styling issues), I do this:


    Button


    myButton.addEventListener('click', myAction, false);
    myButton.addEventListener('keypress', function(event){
    if (event.keyCode === 13) myAction(event);
    }, false);

    Not to mention the additional complexity, nor the approach which is too “concrete” (we explicitly address the case of a keyboard / enter key, so it’s kind of wrong by design), do you see something wrong with this method?

  6. Pierre Bertet

    Oups:


    <a role="button" tabindex="0">Button</a>

  7. Nicholas C. Zakas

    @Pierre - At that point, you're just piling hack on top of hack. Why not just use a button? Except for old IE, you can style buttons to look just like links (or anything else).

  8. LJHarb

    The <button> element has a default type of “submit” – so in your use case, you’d want to add `type=”button”` so that it’s a no-op when JS is disabled.

    I find it to be best to always explicitly specify a <button> type.

    Your comment is awaiting moderation.

  9. Pierre Bertet

    @Nicholas – Why not just use a button?

    That’s what I do most of the time, I totally agree about the pile of hacks. I was just curious to read your opinion about this solution.

    Buttons are extremely hard to style right, and it’s very verbose (we have to deal specifically with: IE on multiple versions, Firefox, Chrome, Safari, Opera, active and focus, and active-then-release-outside). Not to mention the case where you want to style your button exactly like the links of your website (e.g. a small “cancel”, “remove”).

    Anyway, as I said, I prefer the CSS solution most of the time: this is already the most hacked part in the web stack, so I prefer to keep my JS and my HTML as clean as possible (ie not very clean when we talk about a complex web app :] ).

    That said, I totally understand that the a role=button + JS could be preferred, and while I always prefer to use the right tool for the job (which is obviously a button), I happened to choose this solution (can’t remember exactly why).

  10. Vladislav Shkodin

    What about links, that just open popus or show some hidden content (they are usually decorated with dashed or dotted underline)? They don't change url, so should we use button tags for them?

  11. Nicholas C. Zakas

    @Pierre - I may be missing something, but I've never had as much trouble as you described here. I style buttons as links on WellFurnished and it works absolutely fine. In IE8 and below there is a slight alignment discrepancy, but other than that, I find styling buttons to be very easy. There are also a large amount of examples of how to style buttons online.

  12. Nicholas C. Zakas

    @Vladislav - If you click only to execute some JavaScript, then that should be a button. If you click to navigate in some way, that should be a link. So opening a dialog would be a button, same with expand/collapse.

  13. Priit Pirita

    Nicholas, there's my reset styles from past project. Notice that prerquisite of this to work is position:relative.
    If you need to verticall center the text, like height == line-height, then more 1px adjustments is needed :-)

    /* BUTTONS AND LINKS TO LOOK EXACLTY LIKE LINK (default, resetted style) */
    button.a {
    top: 0px;
    position: relative;
    display:inline-block;
    background:none;
    margin:0;
    padding:0;
    border-width:0;
    text-decoration:underline;
    cursor:pointer;
    height: 18px;
    margin-left: 1ex; /* margin-left to previous inline element is 0, so set */
    overflow:visible;

    }

    button.a::-moz-focus-inner{
    padding:0;
    border:none;
    }

    /* remove button pressed effect */
    /* OPERA */
    @media all and (-webkit-min-device-pixel-ratio:10000), not all and (-webkit-min-device-pixel-ratio:0) {
    button.a:active {
    top: -1px;
    left: -1px;
    }
    }
    /*IE8*/
    button.a:active {
    top: -1px\9;
    left: -1px\9;
    }

    /*IE6*/
    button.a {
    _top: 1px;
    _margin-top: -1px;
    }

  14. Koranteng Ofosu-Amaah

    Reminds me that I once wrote 10,000 words on The Unloved HTML Button and Other Folktales. I am continually amazed about designers who misuse links when a button would do.

  15. Pierre Bertet

    @Nicholas Yes, I think that you mean relatively simple buttons, with minor and acceptable style issues, which is indeed easy. I am talking about perfectly styling buttons, with the possibility to do everything you can already do with a <a>. At this point, the discussion may become highly subjective, I guess we should talk about actual demos :-)

    PS: That’s exactly the debate I was trying to avoid ha ha, I’m (almost) as convinced as you about the buttons. I just wanted to know if you had another real issue in mind, out of the enter key. I know it’s conceptually broken.

  16. Nicholas C. Zakas

    @Pierre - We are talking about the same thing. :-) All I can say is that I think people make too much out of styling buttons. It is much easier than it used to be especially when you can use appearance in some browsers.

  17. Nicholas C. Zakas

    @Priit - ah, as you soon as you add relative positioning, you're in for a world of hurt. If you just set display: inline, you're able to eliminate a lot of the other properties because they don't apply to inline elements.

  18. Aninoy Mahapatra

    This completely renders everything that Twitter's Bootstrap library does, invalid. They do EVERYTHING with tags. I wonder what they would have to say when they read this.

  19. Aninoy Mahapatra

    *with tags*

  20. Alex

    Thank you! This is such a huge pet peeve... I really don't get this trend of using whatever element to make buttons, when you can get focus, tab indexing, and accessibility for free using the proper elements.

    BTW, styling aside, any pros to using button over input type=button? I find myself using the latter still, but I don't know if buttons get preferential treatment in screen readers.

  21. Stephen Band

    I'm curious to know what you think of 'hijaxing' links that have *useful* hash references:

    Open dialog

    ...

    This is a pattern has the useful property of containing a reference to the element that has state - in this case the custom_dialog - so when the dialog is open it's easy to give all the buttons that reference it a little hint of style:

    jQuery('.button[href="#' + this.id + '"]').addClass('active_button');

    And of course, if the dialog is hidden only when JS is available, it's content is also accessible when the page is not enhanced. It's a pattern I've been using for every UI element with state - slideshows, menus, tabs, etc. Originally I did it because button elements used to be much harder to style consistently than they are today, but actually think the link offers some advantages.

  22. Stephen Band

    Pants. HTML fail. That first block of code should be:

    <a class="button" role="button" href="#custom_dialog">Open dialog</a>

    <div id="custom_dialog">
    ...
    </div>

  23. Adam Ahmed

    I have an extension to @Vladislav's case. I have a link/button that runs Javascript when you click it, but has a fallback url if you middle click/cmd click to open in a new tab or if you have JS disabled. For example it opens a dialog on left click, but navigates to those same details at another url when you middle click.

    Seems like only a link would work since I need the href value for middle clicking. Am I missing something?

  24. Nicholas C. Zakas

    @Adam - If there's the possibility of navigating to a URL, even as a fallback, then it is correct to use a link.

  25. Jonathan Stark

    If you choose style over accessibility on the web, you're doing it wrong.

  26. Vijay

    So true! I have always wondered why they use tags for Buttons. The worst part, I have seen guys implementing the same in their products. They simply ignore the part that div's are not meant to be used as button. Only because they have a notion set in their mind that "Whatever Google does is always Right!!"

  27. Chris Pearce

    Great post, I'm thinking of creating a website: www.stopusinglinksforbutton... as I'm sick of seeing links being misused or even worse using semantically meaningless span's / div's (shame on you Google but its rife throughout the web). The common styling complaint is a non-issue, I've worked on many website/app builds where buttons needed to be heavily styled & I've always managed to style them perfectly, you just need to know the specific CSS to include, starting with a Normalize style sheet will help you out. The accessibility gains you get from using the correct element for the job (a button/input element in this case) far outweighs any styling concerns which I've never experienced.

  28. Jan Bobrowski

    This discussion is a result of sins of early days of web. What's reason for two different elements that both perform some action on click (or click-like) event? What makes A element a link is HREF attribute. It could have been reused for JavaScript actions. It didn't happened because A has been also used as a fragment marker, so lack of HREF disables button-like semantics (it should be NAME attribute presence instead). BUTTON on the other hand has apparently been introduced solely to look as UI button (whatever they say).

    I prefer to use A for clickable elements, avoid CSS hacks, and wait until web will be mature enough. I hope standards will treat accessibility seriously some day, and don't require to write silly ROLE=BUTTON and the like.

    BTW. Anyone knows how to style disabled BUTTON cross browser?

  29. Pete

    @Aninoy Looks like it is up to the developer just what exactly they use:
    http://twitter.github.com/b...

    Their button groups sample is all pretty much done with button:
    http://twitter.github.com/b...

  30. Web Axe

    Thanks for the great article, Nicholas. Twittering...

  31. Peter Winnberg

    Interesting blog post and discussion. I think that the button element is an interesting but problematic element in (X)HTML.

    It is interesting because you can use markup in the button label, even things like images can be used.
    But it is also problematic because it can leave buttons that does nothing when Javascript is disabled/not available and in user agents that lack support for the button element you can end up with just the button label and no styling. The button element was also added fairly late to HTML so it makes little sense to use this element for input/reset buttons instead of the buttons created using the input element. That leaves buttons with other uses.

    Take this example, you want to have button on the bottom of a blog that says “Load 5 more blog posts” that retrieves 5 more blog posts and adds them to the current page. Just like it have been pointed out in this discussion this should be a link because it can be solved by navigating to other pages and a script could change what happens when you activate the button. This would work in more or less any user agent regardless of how old and outdated it is.

    But should this link have the role=button? Well I think that depends on if Javascript support is available or not. If no Javascript support is available then it is just a regular link to another URL, it makes no sense to use role=button. The script should add this role (or maybe change the a element to a button element?) when it changes what happens when the link is activated.

Understanding JavaScript Promises E-book Cover

Demystify JavaScript promises with the e-book that explains not just concepts, but also real-world uses of promises.

Download the Free E-book!

The community edition of Understanding JavaScript Promises is a free download that arrives in minutes.