Skip to main content

Journal

BBC WebCore Design System

Here’s the BBC’s Storybook UI explorer containing the components and layouts for making the front end of a BBC web experience.

Buttons and links: definitions, differences and tips

On the web buttons and links are fundamentally different materials. However some design and development practices have led to them becoming conceptually “bundled together” and misunderstood. Practitioners can fall into the trap of seeing the surface-level commonality that “you click the thing, then something happens” and mistakenly thinking the two elements are interchangeable. Some might even consider them as a single “button component” without considering the distinctions underneath. However this mentality causes our users problems and is harmful for effective web development. In this post I’ll address why buttons and links are different and exist separately, and when to use each.

Problematic patterns

Modern website designs commonly apply the appearance of a button to a link. For isolated calls to action this can make sense however as a design pattern it is often overused and under-cooked, which can cause confusion to developers implementing the designs.

Relatedly, it’s now common for Design Systems to have a Button component which includes button-styled links that are referred to simply as buttons. Unless documented carefully this can lead to internal language and comprehension issues.

Meanwhile developers have historically used faux links (<a href="#">) or worse, a DIY clickable div, as a trigger for JavaScript-powered functionality where they should instead use native buttons.

These patterns in combination have given rise to a collective muddle over buttons and links. We need to get back to basics and talk about foundational HTML.

Buttons and anchors in HTML

There are two HTML elements of interest here.

Hyperlinks are created using the HTML anchor element (<a>). Buttons (by which I mean real buttons rather than links styled to appear as buttons) are implemented with the HTML button element (<button>).

Although a slight oversimplification, I think David MacDonald’s heuristic works well:

If it GOES someWHERE use a link

If it DOES someTHING use a button

A link…

  • goes somewhere (i.e. navigates to another place)
  • normally links to another document (i.e. page) on the current website or on another website
  • can alternatively link to a different section of the same page
  • historically and by default appears underlined
  • when hovered or focused offers visual feedback from the browser’s status bar
  • uses the “pointing hand” mouse pointer
  • results in browser making an HTTP GET request by default. It’s intended to get a page or resource rather than to change something
  • offers specific right-click options to mouse users (open in new tab, copy URL, etc)
  • typically results in an address which can be bookmarked
  • can be activated by pressing the return key
  • is announced by screen readers as “Link”
  • is available to screen reader users within an overall Links list

A button…

  • does something (i.e. performs an action, such as “Add”, “Update” or "Show")
  • can be used as <button type=submit> within a form to submit the form. This is a modern replacement for <input type=submit /> and much better as it’s easier to style, allows nested HTML and supports CSS pseudo-elements
  • can be used as <button type=button> to trigger JavaScript. This type of button is different to the one used for submitting a <form>. It can be used for any type of functionality that happens in-place rather than taking the user somewhere, such as expanding and collapsing content, or performing a calculation.
  • historically and by default appears in a pill or rounded rectangle
  • uses the normal mouse pointer arrow
  • can be activated by pressing return or space.
  • implictly gets the ARIA button role.
  • can be extended with further ARIA button-related states like aria-pressed
  • is announced by screen readers as “Button”
  • unlike a link is not available to screen reader users within a dedicated list

Our responsibilities

It’s our job as designers and developers to use the appropriate purpose-built element for each situation, to present it in a way that respects conventions so that users know what it is, and to then meet their expectations of it.

Tips

  • Visually distinguish button-styled call-to-action links from regular buttons, perhaps with a more pill-like appearance and a right-pointing arrow
  • Avoid a proliferation of call-to-action links by linking content itself (for example a news teaser’s headline). Not only does this reduce “link or button?” confusion but it also saves space, and provides more accessible link text.
  • Consider having separate Design System components for Button and ButtonLink to reinforce important differences.
  • For triggering JavaScript-powered interactions I’ll typically use a button. However in disclosure patterns where the trigger and target element are far apart in the DOM it can make sense to use a link as the trigger.
  • For buttons which are reliant on JavaScript, it’s best to use them within a strategy of progressive enhancement and not render them on the server but rather with client-side JavaScript. That way, if the client-side JavaScript is unsupported or fails, the user won’t be presented with a broken button.

Update: 23 November 2024

Perhaps a better heuristic than David MacDonald’s mentioned above, is:

Links are for a simple connection to a resource; buttons are for actions.

What I prefer about including a resource is that the “goes somewhere” definition of a link breaks down for anchors that instruct the linked resource to download (via the download attribute) rather than render in the browser, but this doesn’t. I also like the inclusion of simple because some buttons (like the submit button of a search form) might finish by taking you to a resource (the search results page) but that’s a complex action not a simple connection; you’re searching a database using your choice of search query.

References

HTML with Superpowers (from Dave Rupert)

Here’s a great new presentation by Dave Rupert (of the Shop Talk show) in which he makes a compelling case for adopting Web Components. Not only do they provide the same benefits of encapsulation and reusability as components in proprietary JavaScript frameworks, but they also bring the reliability and portability of web standards, work without build tools, are suited to progressive enhancement, and may pave the way for a better web.

Dave begins by explaining that Web Components are based on not just a set of technologies but a set of standards, namely:

  • Custom Elements (for example <custom-alert>)
  • Shadow DOM
  • ES Modules
  • the HTML <template> element

Standards have the benefit that we can rely on them to endure and work into the future in comparison to proprietary technologies in JavaScript frameworks. That’s good news for people who like to avoid the burnout-inducing churn of learning and relearning abstractions. Of course the pace of technology change with web standards tends to be slower, however that’s arguably a price worth paying for cross-platform stability and accessibility.

Some of Web Components’ historical marketing problems are now behind them, since they are supported by all major browsers and reaching maturity. Furthermore, web components have two superpowers not found in other JavaScript component approaches:

Firstly, the Shadow DOM (which is both powerful and frustrating). The Shadow DOM provides encapsulation but furthermore in progressive enhancement terms it enables the final, enhanced component output which serves as an upgrade from the baseline Light DOM HTML we provided in our custom element instance. It can be a little tricky or confusing to style, however, although there are ways.

Secondly, you can use web components standalone, i.e. natively, without any frameworks, build tools, or package managers. All that’s required to use a “standalone” is to load the <script type=module …> element for it and then use the relevant custom element HTML on your page. This gets us closer to just writing HTML rather than wrestling with tools.

Dave highlights an education gap where developers focused on HTML, CSS, and Design Systems don’t tend to use Web Components. He suggests that this is likely as a result of most web component tutorials focusing on JavaScript APIs for JavaScript developers. However we can instead frame Web Component authoring as involving a layered approach that starts with HTML, adds some CSS, then ends by applying JavaScript.

Web Components are perfectly suited to progressive enhancement. And that progressive enhancement might for example apply lots of complicated ARIA-related accessibility considerations. I really like the Tabs example where one would create a <generic-tabs> instance which starts off with simple, semantic, resilient HTML that renders headings and paragraphs…

<generic-tabs>
  <h2>About</h2>
  <div>
    <p>About content goes here. Lorem ipsum dolor sit amet…</p>
  </div>

  <h2>Contact</h2>
  <div>
    <p>Contact content goes here. Lorem ipsum dolor sit amet…</p>
  </div> 
</generic-tabs>

…but the Web Component’s JavaScript would include a template and use this to upgrade the Light DOM markup into the final interactive tab markup…

<generic-tabs>
  <h2 slot="tab" aria-selected="true" tabindex="0" role="tab" id="generic-tab-3-0" aria-controls="generic-tab-3-0" selected="">About</h2>
  <div role="tabpanel" aria-labelledby="generic-tab-3-0" slot="panel">
    <p>About content goes here. Lorem ipsum dolor sit amet…</p>
  </div>
  <h2 slot="tab" aria-selected="false" tabindex="-1" role="tab" id="generic-tab-3-1" aria-controls="generic-tab-3-1">Contact</h2>
  <div role="tabpanel" aria-labelledby="generic-tab-3-1" slot="panel" hidden>
    <p>Contact content goes here. Lorem ipsum dolor sit amet…</p>
  </div>
</generic-tabs>

The idea is that the component’s JS would handle all the complex interactivity and accessibility requirements of Tabs under the hood. I think if I were implementing something like Inclusive Components’ Tabs component these days I’d seriously consider doing this as a Web Component.

Later, Dave discusses the JavaScript required to author a Custom Element. He advises that in order to avoid repeatedly writing the same lengthy, boilerplate code on each component we might use a lightweight library such as his favourite, LitElement.

Lastly, Dave argues that by creating and using web components we are working with web standards rather than building for a proprietary library. We are creating compatible components which pave the cowpaths for these becoming future HTML standards (e.g. a <tabs> element!) And why is advancing the web important? Because an easier web lowers barriers: less complexity, less tooling and setup, less gatekeeping—a web for everyone.

How to debug event listeners with your browser’s developer tools (on Go Make Things)

On the page, right-click the element you want to debug event listeners for, then click Inspect Element. In chromium-based browsers like MS Edge and Google Chrome, click the Event Listeners tab in Developer Tools. There, you’ll see a list of all of the events being listened to on that element. If you expand the event, you can see what element they’re attached to and click a link to open up the actual event listener itself in the JavaScript.


Conditional border-radius in CSS (by Ahmad Shadeed via CSS-Tricks)

Here’s a “media query free” CSS one-liner which lets you set an element to have no border-radius when it is the full width of the viewport, but otherwise to have a border-radius.

It uses the same 9999 multiplication technique as Every Layout do to create a toggle.

Great for Card components which need to be full-width and non-rounded only on narrow viewports.

Design Engineering, on the Clearleft Podcast

Loved this short listen from Clearleft, on a subject close to my heart! New job titles can feel a bit “emperor’s new clothes” but with Design Engineering I think Clearleft, GitHub et al. might be onto something. It was fascinating hearing people from both design and engineering backgrounds give their perspectives, and how ultimately they’re addressing the same thing—the need to “finesse the overlaps/gaps” between design and the realisation of that design in engineering, especially in light of the complexities of the modern front-end.

(via @Stugoo)

Broken Copy, on a11y-101.com

Here’s an accessibility tip that’s new to me. When the content of a heading, anchor, or other semantic HTML element contains smaller “chunks” of span and em (etc), the VoiceOver screen reader on Mac and iOS annoyingly fails to announce the content as a single phrase and instead repeats the parent element’s role for each inner element. We can fix that by adding an inner “wrapper” element inside our parent and giving it role=text.

Make sure not to add this role directly to your parent element since it will override its original role causing it to lose its intended semantics.

The text role is not yet in the official ARIA spec but is supported by Safari.

(via @Seraphae and friends on Twitter)