HTML button with a link

Style an anchor to look like a button. A button element does not navigate on its own, and the workarounds around that cost you middle click, keyboard behaviour and screen reader meaning.

To make an HTML button with a link, style an anchor to look like a button rather than putting a link inside a <button> element.

<a href="/pricing" class="btn">See pricing</a>
.btn {
  display: inline-block;
  padding: 12px 20px;
  background: #2563eb;
  color: #fff;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
}

That is a button to the reader and a link to the browser, which is the combination you want.

An anchor styled as a filled button, with the destination URL shown in the browser status bar on hover.
An anchor styled as a filled button, with the destination URL shown in the browser status bar on hover.

Why not a button element

A <button> does not navigate. On its own it does nothing outside a form, so making it go somewhere means adding script:

<button onclick="location.href='/pricing'">See pricing</button>

That looks equivalent and is not. Four things are lost.

  1. Middle click and right click. There is no URL, so open in new tab and copy link address are not offered.
  2. The status bar. Hovering shows nothing, so the reader cannot see where they are about to go.
  3. Screen reader meaning. It is announced as a button, so the reader expects an action and gets a page change.
  4. Script dependence. With script blocked or failing, the link is dead. An anchor works with no script at all.
Styled anchor Button with onclick
Open in new tab Yes No
Shows destination on hover Yes No
Works without JavaScript Yes No
Announced as Link Button
Crawlable by search engines Yes No
Keyboard activation Enter Enter and space

The only row favouring the button is the last one, and it is a minor difference.

The markup patterns to avoid

A button inside an anchor. Interactive elements cannot nest. Browsers recover from it differently and assistive technology reports it inconsistently.

<!-- do not do this -->
<a href="/pricing"><button>See pricing</button></a>

An anchor with no href. Without href an anchor is not focusable and is not announced as a link. If you have no destination, you have an action, so use a button.

A form wrapped around a link for navigation. A GET form to change page adds a query string you did not ask for, and hides the destination.

The inline version

If the page has to travel as a single file, put the styling in the tag so it cannot be separated from it.

<a href="/pricing"
   style="display:inline-block;padding:12px 20px;background:#2563eb;color:#fff;border-radius:8px;text-decoration:none;font-weight:600">
  See pricing
</a>

This is the standard approach in HTML email, where stylesheet support is inconsistent. Inline CSS covers when to prefer it.

The trade off is that a style attribute cannot express hover or focus states. Those need a <style> block in the document.

The same button written with a style attribute on the anchor, so the appearance travels with the element.
The same button written with a style attribute on the anchor, so the appearance travels with the element.

States

A button that is the main action on a page needs three states beyond its default.

.btn:hover { background: #1d4ed8; }
.btn:active { background: #1e40af; }
.btn:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}

Write them after the base rule. Pseudo-class order matters, as covered in link hover colour.

Do not remove the outline without replacing it. On a primary action, an invisible focus state means keyboard users cannot tell when they have reached it.

Labels that work out of context

Screen reader users can pull up a list of every link on the page. In that list, only the link text appears.

"Click here" three times is useless. "See pricing", "Download the Q3 report", "Email the team" are not.

If the visible text genuinely cannot carry it, an aria-label supplies an accessible name. Use it as a fallback, not as a habit.

Size and spacing

Buttons are tapped as often as they are clicked. A comfortable target is around 44 pixels tall, which padding: 12px 20px with normal text reaches.

Leave space between adjacent buttons. Two primary actions touching each other produce mistaps on a phone, and the mistake is only visible after it happens.

Use one filled button per section. If everything is filled, nothing is primary, and secondary actions read better as an outlined button or a plain link.

A primary filled button next to a secondary outlined one, with clear space between the two targets.
A primary filled button next to a secondary outlined one, with clear space between the two targets.

Buttons that are not navigation

Some buttons really are actions, and those should be <button> elements.

  • Mail and phone actions are still navigation. mailto: and tel: are links, so they stay anchors. Mailto with multiple recipients shows the styled version.
  • Submitting a form is an action. Use <button type="submit">.
  • Toggling a panel is an action. Use a button with aria-expanded.
  • Copying a value is an action, with no destination at all.

The test is one question: does the reader end up somewhere else? If yes, it is a link.

After the page is written

A button only works once the page is somewhere the reader can open it. Sending the HTML file itself means the button never gets clicked, because the file often never renders.

Turning HTML into a link puts the page at an address. In a NOS document the pasted HTML renders as written, buttons, hover states and all, and editing the page does not move its address.

If the button collects information rather than sending people onward, turning a form into a link covers that case.

A block to copy

<a href="/pricing" class="btn">See pricing</a>
<a href="/docs" class="btn btn-quiet">Read the docs</a>
.btn {
  display: inline-block;
  padding: 12px 20px;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
  background: #2563eb;
  color: #fff;
}
.btn-quiet {
  background: transparent;
  color: #2563eb;
  box-shadow: inset 0 0 0 1px currentColor;
}
.btn:hover { filter: brightness(.92); }
.btn:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }

One primary and one secondary, both anchors, both with real destinations. The secondary uses an inset shadow rather than a border, so the two buttons stay the same height.

Removing the underline here is safe, because the shape already identifies the element. Links without underlines covers where that reasoning stops applying.

Questions people ask

Should I use a button or an anchor for a link?

An anchor. If clicking it takes the reader somewhere, it is a link and belongs in an a element with an href. A button element is for an action inside the page, such as submitting a form or opening a panel. Style decides the appearance; the element decides the behaviour.

How do I make a link look like a button?

Give the anchor display inline-block, padding, a background colour, a border radius and text-decoration none. That produces the shape without giving up any link behaviour, so right click, middle click and open in new tab all keep working.

What about wrapping a button inside an anchor?

Do not. Interactive elements cannot be nested inside each other in valid HTML, and the result is unpredictable across browsers and confusing for screen readers. Style the anchor directly instead.

When is a real button element correct?

When nothing navigates. Submitting a form, opening a dialog, toggling a section, copying a value. Those are actions, and a button announces them as such, so the reader is not told to expect a new page that never arrives.

Keep reading