albert.walickihire me
← all solutions
html

How to make a button or div work like a link?

Use a real link styled as a button instead of a clickable div or button, make a whole card clickable, and navigate with JavaScript only when you really need to.

If clicking it takes the user to another page, make it a link: an <a href> styled to look like a button.

<a href="/pricing" class="button">See pricing</a>
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.25rem;
  border-radius: 0.5rem;
  background: #2563eb;
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}

.button:hover {
  background: #1d4ed8;
}

.button:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}

A real link gives you things a clickable <div> or <button> doesn't:

  • Cmd or Ctrl + click and middle-click open it in a new tab, and right-click offers "Copy link".
  • The browser shows the URL when you hover over it.
  • Search engine crawlers follow it.
  • Screen readers announce it as a link and list it with the other links on the page.
  • It works before your JavaScript has loaded, and if it fails to load.

In React, the <Link> components from Next.js and React Router render an <a>, so style that.

Avoid onclick navigation

<!-- Don't do this -->
<div class="button" onclick="location.href = '/pricing'">
  See pricing
</div>

A <div> can't be focused with the keyboard and has no role, so screen readers don't know it's clickable, and none of the list above works. Adding tabindex="0", role="link" and a key handler fixes part of it, but then you're rebuilding a link, badly. A <button> with the same onclick can be focused, but it's announced as a button and still can't be opened in a new tab.

Also, don't put a <button> inside an <a> or the other way around. Interactive elements inside each other are invalid HTML and behave differently across browsers and screen readers.

A clickable card

You can wrap a whole card in <a>, but screen readers then read everything in the card as the link text. A cleaner pattern is a real link on the title, stretched over the card with a pseudo-element:

<article class="card">
  <img src="/posts/grid.jpg" alt="">
  <h3>
    <a href="/blog/css-grid" class="card-link">
      A practical guide to CSS grid
    </a>
  </h3>
  <p>Build real page layouts with grid-template-areas.</p>
</article>
.card {
  position: relative;
}

.card-link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.card-link:focus-visible {
  outline: none;
}

.card:has(.card-link:focus-visible) {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

The whole card is clickable, the link text is only the title, and keyboard focus outlines the whole card. Other links or buttons inside the card need position: relative and a z-index to sit above the stretched link. The downside is that the card's text can't be selected with the mouse, because the link covers it.

Navigating from a form

If a button should go to a URL built from form fields, let the form do it. A form with method="get" navigates to its action with the fields in the query string, and formaction gives a button its own URL:

<form action="/search" method="get">
  <input type="search" name="q">
  <button>Search</button>
  <button formaction="/search/advanced">Advanced search</button>
</form>

The first button goes to /search?q=... and the second to /search/advanced?q=....

When you really need JavaScript

Sometimes navigation has to wait for something async, like saving a draft. Then navigate from code:

async function saveAndContinue() {
  await saveDraft();
  location.assign('/checkout');
}
  • location.href = '/checkout' does the same as location.assign().
  • location.replace('/checkout') doesn't add a history entry, so the back button skips the current page.
  • window.open(url, '_blank', 'noopener') opens a new tab. Browsers may block it as a popup when it doesn't run directly in response to a click, for example after a slow request.

For redirects that happen as soon as a page loads, see how to redirect to another page in HTML.

more solutions
work with me

Got something that needs building?

Frontend builds, full-stack features in Django, design-system work. Available for work.

See my workGet in touch