Skip to content
New: free AI crawler checker. See if ChatGPT and Claude can read your site. Check now

Popular

Programming Languages

HTML button link: how to make a button go to a page

Published  Updated  14 min read 2,439 views Save
HTML button link: index.html and styles.css panes with a link styled as a button and its CSS rules, the rendered button below, with a Copy the button code button
On this page

Quick answer

To make an HTML button link to another page, use a link, not a button: <a href="/prices" class="btn">, styled with CSS to look like a button. Keep the real <button> for sending a form or doing something on the page. And never put a button inside a link, because that's invalid HTML.

Say your site needs a "See our prices" button that opens your price page.

There are three common ways to build that HTML button link, and most tutorials list them as equals. They aren't.

On October 26, 2026, we built six versions of the same button on one test page and drove them with the keyboard and mouse in Chrome 155. A small server on our own machine logged every request, so we could see exactly where each click went.

Key takeaways

  • If it opens another URL, build a link; if it acts, build a button.
  • Never wrap a button inside a link, because that is invalid HTML.
  • Use :focus-visible for a keyboard focus ring, and never just outline: none.
  • Text needs 4.5:1 contrast, and the focus ring and button edges need 3:1.
  • Icon-only buttons need a name, such as an aria-label like "Open menu".

The choice comes down to one question: does it take people to another URL? If it does, it's a link. If it sends a form or changes something on the current page, it's a button.

MDN's reference for the a element limits hyperlinks to "navigation to a real URL" and nothing else. W3C's button pattern adds that "both the appearance and role of a widget match the function it provides."

That doesn't stop you from making a link look like a button.

Chrome's accessibility tree still called our styled link a "link", which is the truth, and sighted visitors still get a big colored target.

Diagram: if a control goes to another page, use an a element with href styled as a button; if it sends a form or acts on the page, use a button elementDiagram by DigitalTreed: the link-or-button rule, based on MDN's a element reference and W3C's button pattern, read October 2026.

One thing you can't do is wrap a button in a link.

The HTML standard says an a element must have "no interactive content descendant," and its list of interactive content includes button. In our test, Chrome gave that nested pair two Tab stops for one destination.

So keyboard users press Tab twice to get past one button.

A div with a click handler is worse. Chrome's accessibility tree listed our div as a "generic" element with an empty name. The Tab key skipped it. MDN's button role page says role="button" "does not provide other typical button functionality such as click events and keyboard handling," so you'd rebuild all of it by hand.

What about the older <input type="button">? It still works, but MDN's button reference says button elements are "much easier to style than <input> elements." They can hold an icon or other HTML.

Our post on making links in HTML handles text links, new tabs, and email or phone links. This one sticks to buttons.

Now the code. Here's the HTML for the button most small-business sites need, and it's an ordinary link with a class:

<a href="/prices" class="btn">See our prices</a>

And here's the CSS that makes it look like a button:

.btn {
  display: inline-block;
  padding: 12px 24px;
  border: 2px solid #1d4d2b;
  border-radius: 8px;
  background: #1d4d2b;
  color: #ffffff;
  font: 600 1rem/1.25 system-ui, sans-serif;
  text-decoration: none;
  cursor: pointer;
}

.btn:hover {
  background: #2f6b3f;
  border-color: #2f6b3f;
}

.btn:focus-visible {
  outline: 3px solid #b8590b;
  outline-offset: 3px;
}

Not sure where that CSS goes? Our post on how to link CSS to an HTML page covers the file and the tag, and any of these free HTML editors will open both files.

A few lines carry most of the weight.

  • display: inline-block makes the link behave like a box, so its padding gives it a button's shape.
  • text-decoration: none removes the underline that browsers draw under links.
  • cursor: pointer keeps the hand cursor when you put the same class on a real button.
  • padding: 12px 24px made our link 48 pixels tall in Chrome, which matters for the size rule below.

Example: Hollow Creek Tree Care is a made-up tree trimming company in Lynchburg, Virginia. Its home page has two buttons. "See our prices" opens the price page, so it's a link with class="btn". "Request an estimate" sends a short form, so it's a real submit button with the same class. They look the same, and each one tells the browser the truth about its job.

Style it with hover and focus-visible

The two state rules in that CSS do different jobs. The :hover rule changes the color while a mouse pointer sits on the button, and :focus-visible draws a ring when someone reaches it with the keyboard.

MDN's :hover reference warns that it "is problematic on touchscreens," so don't hide anything important behind it.

Treat hover as a bonus.

Focus matters more.

As MDN's :focus-visible page puts it, "when a button is clicked using a pointing device, the focus is generally not visually indicated."

That's why :focus-visible beats plain :focus here: keyboard users get the ring, and mouse users don't see it flash on every click.

Our test matched that.

After one Tab press, our link matched :focus-visible and showed a 3px orange outline. After a mouse click, a button had focus but didn't match :focus-visible.

Whatever you do, don't write outline: none and stop there. MDN's outline reference says that "will remove the browser's default focus style." WCAG 2.2's Focus Visible rule (Level AA) asks for a keyboard focus indicator people can see.

Screenshot of our test page in Chrome: a green See our prices link styled as a button with an orange focus ring, plus two form buttons, numbered 1 to 3Screenshot: our Hollow Creek Tree Care test page in headless Chrome 155 after one Tab press, served on 127.0.0.1, October 26, 2026. Labels by DigitalTreed.

Check the colors next.

WCAG's text contrast rule asks for at least 4.5:1, and its non-text contrast rule asks for 3:1 for the parts that show a control and its states. A focus ring counts. Our white text on dark green scored 9.77:1, the hover green 6.37:1, and the orange ring 4.70:1 against the white page.

Want to test your own brand colors? Our branding strategy guide runs a palette through the same contrast math.

Size is the last check.

WCAG 2.2's Target Size (Minimum) rule asks for at least 24 by 24 CSS pixels, and MDN's button reference recommends 44×44. Our styled link measured 160.6 by 48 pixels, so it clears both.

Inside a form, formaction sends one button somewhere else

A real button earns its place inside a form. Hollow Creek's estimate form has two submit buttons: one sends the request, and the other saves the address for later.

<form action="/estimate" method="post">
  <label for="address">Street address</label>
  <input id="address" name="address" autocomplete="street-address" required>

  <button type="submit" class="btn">Request an estimate</button>
  <button type="submit" class="btn" formaction="/save-for-later" formnovalidate>Save for later</button>
</form>

The formaction attribute is what splits them. MDN's button reference defines it as "The URL that processes the information submitted by the button." It overrides the form's own action. The formnovalidate attribute lets that second button skip the required check.

In our test, "Request an estimate" with an empty address sent nothing, because Chrome stopped it. With "12 Oak Lane" typed in, it posted to /estimate. "Save for later" posted to /save-for-later even with the field empty.

Two rules keep this from biting you. The HTML standard says formaction "must not be specified if the element is not a submit button," so keep it on submit buttons. And a button inside a form submits by default, so give any button that isn't meant to send the form type="button".

You'll also see a whole form used as a link, like this:

<form action="/prices">
  <button class="btn">See our prices</button>
</form>

It's valid HTML, but it's a hack.

When we clicked it, Chrome asked our server for /prices? with a stray question mark, and the address bar kept it. Use a link.

Building a full contact form? Our guide to an HTML contact form that actually sends email covers the part that runs on the server.

onclick navigation works, with four catches

The third method in most tutorials is a button that changes the page with JavaScript:

<button type="button" class="btn" onclick="location.href='/prices'">See our prices</button>

It does work.

Setting location.href "navigates to the provided URL," as MDN's Location.href reference puts it, and our click reached /prices.

But it gives up four things a plain link gets for free.

  • New tabs. On macOS, a Cmd-click on our styled link opened /prices in a new tab. The same Cmd-click on the onclick button just loaded the page in the current tab.
  • JavaScript off. With scripts turned off in Chrome, the link still went to /prices. The onclick button did nothing at all.
  • Search crawling. Google's crawlable links guide says it "can only crawl your link if it's an <a> HTML element" with an href attribute. It also says it can't reliably extract URLs from tags that act as links "because of script events."
  • The right role. Chrome named it a button, so a screen reader user hears "button" and expects an action, not a new page.

Run your home page through our internal link checker and make sure your main buttons show up in its list of links.

If one is missing, look for an onclick.

Learning JavaScript anyway? Our JavaScript tutorial for beginners gets you started, and our post on location.reload(true) explains more of the location object. For links, though, keep the script out of it.

Here's how we ran it. On October 26, 2026, we loaded one test page in headless Chrome 155 on our Mac, served by a small Python server bound to 127.0.0.1. We sent real key presses and mouse clicks through Chrome's DevTools protocol, and the server wrote every request to a log. Then we read Chrome's accessibility tree for each element's role and name.

Two results are worth knowing even if you skip the rest.

First, Space did nothing on the styled link, while it pressed the onclick button. That's by design. MDN's button role page says buttons respond to Space or Enter, but links are expected to fire with the Enter key. And W3C's link pattern gives Enter as the key that "Executes the link."

A link styled as a button still behaves like a link.

Second, the a element with a click handler and no href was skipped by the Tab key. The HTML standard calls an a without href "a placeholder for where a link might otherwise have been," and Chrome treated it that way.

Results grid from our Chrome test of 6 HTML button link patterns: what Tab, Enter, Cmd-click, a click and JavaScript off did for eachSource: DigitalTreed test in headless Chrome 155 on 127.0.0.1, October 26, 2026. Crawl column from Google's crawlable links guide.

We also ran every snippet in this post through HTML Tidy 5.8.0 and our own check of the HTML standard's nesting rules. Chrome accepted every CSS declaration. We didn't test Safari, Firefox or a real screen reader.

If your site's buttons need more than a class change, redesigning the templates is the kind of job our web and app design team takes on. We'd earn money from that, so fix the easy ones yourself first.

Give every button a name people can say

That leaves the words on the button. Every button and link needs an accessible name, the text a screen reader reads out and voice software listens for. MDN's accessible name glossary says "It is best to use visible text as the accessible name," and for links and buttons that's the text inside them.

So "See our prices" beats "Learn more", because it says where the button goes.

Icon-only buttons are the trap.

MDN's button reference says "Buttons that only display an icon do not have an accessible name." Give one a name with aria-label:

<button type="button" class="icon-btn" aria-label="Open menu">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
    <path d="M3 6h18M3 12h18M3 18h18" stroke="currentColor" stroke-width="2"/>
  </svg>
</button>

Chrome named it "Open menu" in our test. The aria-hidden attribute on the SVG, per MDN's aria-hidden reference, removes the drawing "from the accessibility tree."

For an icon link, you can put real text inside the link and hide it visually instead:

<a href="/" class="icon-btn">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
    <path d="M3 11l9-8 9 8v10h-6v-6H9v6H3z" fill="currentColor"/>
  </svg>
  <span class="visually-hidden">Home</span>
</a>
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

Chrome named that one "Home".

One more rule catches people who add aria-label to a button that already shows text. WCAG 2.2's Label in Name rule (Level A) asks that the accessible name contain the visible text. The reason: "People who operate with voice interaction use the visible labels in their commands."

We tested that mistake. A link showing "Get a free estimate" with aria-label="Request quote" got the name "Request quote" in Chrome. A voice user saying "click Get a free estimate" wouldn't find it.

Name tags showing what Chrome computed for each control: See our prices, Open menu, Home, and Request quote replacing the visible text Get a free estimateSource: accessible names from Chrome 155's accessibility tree in our test, October 26, 2026. Name-tag drawing by DigitalTreed.

MDN's aria-label reference tells you not to "overuse" aria-label. If a button has visible text, let that be its name.

Building the whole site? Our guide to building a website with AI walks through the pages these buttons sit on, and our HTML tutorial for beginners covers the basic tags.

Frequently asked

Can I put a button inside a link?

No. The HTML standard bars interactive content, which includes button, inside an a element. In our Chrome test the nested pair took two Tab presses for one destination. Style the link as a button instead.

How do I open a button link in a new tab?

Use a link styled as a button. Visitors can Cmd-click it on a Mac, which opened a new tab in our test. If you add target="_blank", warn people. MDN's a element reference says such links "should indicate what will happen when the link is followed." One way is to add "(opens in new tab)" to the link text.

Does Google follow a button with onclick?

Not reliably. Google's crawlable links guide lists <a onclick="goto('https://example.com')"> as not recommended. It says Google can generally crawl a link only if it's an a element with an href. Our post on finding internal links shows how to check yours.

Got a site full of buttons that don't behave? Send us your site and ask for a quote. That's paid work for us, so try the code above first.

How we made this guide: each quote is copied exactly from MDN, WHATWG, W3C WAI or Google's crawlable links guide, as those pages read on October 26, 2026. We built a test page with six button patterns and ran it in headless Chrome 155 on our own Mac, with a local Python server logging every request. We checked each snippet with HTML Tidy 5.8.0, our own nesting check and Chrome's CSS parser, and computed the contrast ratios. We didn't test other browsers or listen with a screen reader. Eram N., who wrote the original post, reads this version and signs off on it before we publish.

First published July 2020. Rewritten October 26, 2026: the old page listed button attributes with no working code. This version adds tested code for a link styled as a button, plus formaction, onclick's catches, hover and focus styles, and accessible names.

Sources

Want more customers online?

  • SEO, ads, social media and websites
  • A fixed quote before any work starts
  • A plain report every month

What is wrong?

Step 1 of 2. Pick the closest reason.

Reason

Was this post helpful?

Written by

I have been serving web content with my passionate writing skills since 2020. My skills have benefited clients from 20 countries, resulting in 10x audience interactions, improved readability, and SEO-friendly content.

More posts by Eram N.

Comments

No comments yet. Start the conversation below.

Leave a comment

Plain text only, no links or email addresses. Comments are reviewed first.