The rule fits in a line. A link goes somewhere. A button does something.
That is not our formulation, which is worth knowing when the argument reaches a stubborn developer. The ARIA specification defines a link as an interactive reference that causes the user agent to navigate to a resource, defines a button as an input allowing user-triggered actions, and then adds a note settling the whole debate. If pressing the link triggers an action but does not change browser focus or page location, authors are advised to consider using the button role instead. That sentence is the argument, in the standard, in one line.
So if it changes the URL, it is a link. If it submits, opens, closes, toggles, saves, deletes, or plays, it is a button. Styling has nothing to do with it. A link styled as a button is still a link.
| Link (a href) | Button | |
|---|---|---|
| Announced as | "link" | "button" |
| Activated by | Enter | Enter and Space |
| Structure inside it | Survives. A heading inside a link is still a heading | Flattened. A button's children are presentational, so a heading inside one is a string |
| Needs an accessible name | Yes | Yes |
| Right-click menu | Open in new tab, copy address | Nothing useful |
| Middle-click | Opens in a new tab | Nothing |
| Listed under | The screen reader's list of links | Its list of buttons or form controls, depending on the reader |
| Purpose | Navigation | Action |
Two of those rows are worth labeling. The keyboard row and the structure row come from the specification, so they hold everywhere. The right-click, middle-click and list rows are how browsers and screen readers behave rather than anything a standard requires, and the exact list a control appears under varies between readers. Both are true. Only one of them is quotable at somebody.
Why It Actually Matters
Screen reader users navigate by pulling up lists. All the links on the page. All the form controls. All the headings. Get the element wrong and your control is in the wrong list, so Add to basket built as a link is not among the buttons where somebody went looking for it.
The keyboard difference is not a technicality either. Buttons answer Enter and Space, and links answer Enter only. Press Space on a link and the page scrolls, which is what a keyboard user gets when they reasonably assume the button-shaped thing is a button.
And link behaviors are real expectations people rely on rather than affordances they merely notice. Open in a new tab. Copy the address. See where it goes in the status bar before committing. None of that works on something that is not going anywhere.
One difference nobody writes about, and it is the one that bites hardest on modern layouts. A button's children are presentational by specification, so everything inside it collapses to a text string. Build a clickable card as one big button and the heading inside it stops being a heading, the list stops being a list, and the image's alt text stops standing on its own. Build the same card so the heading contains the link and all of that survives. That is why the wrapping-link pattern has outlasted every attempt to replace it.
Both of Them Need a Name
Whichever you pick, the specification marks an accessible name as required for both roles. A button showing only an icon has no name, and a link containing only an image with no alt text has an empty one. Both fail 4.1.2 Name, Role, Value, and both leave a speech input user with nothing to say out loud to activate the control. Settling which element to use and stopping there is how a page ends up with the right markup and no names in it. Our fix guides cover naming icon-only controls.
The Failure That is Worse Than Both
A div with a click handler. It works with a mouse and is invisible to everything else. It is not focusable, so a keyboard user never reaches it, and it is not announced, so a screen reader user never knows it exists. Our keyboard guide covers what that costs.
Making it work means adding tabindex, a role, key handling for Enter and Space, and a focus style. That is four things you get free from a native element, and the framing W3C uses is the one to remember. A role is a promise. Writing role="button" on a div promises that you have also written the JavaScript providing the keyboard behavior a button has, because ARIA roles do not cause browsers to provide any behavior at all. Our fix guide covers divs built as buttons, and using the native control is the shorter route.
For the link case there is a fifth thing, and it is the one people are most surprised by. role="link" does not make navigation happen either. It does not bring the context menu actions the table above promised, because those come from the element rather than from the role. So a div carrying role="link" announces as a link, goes nowhere on its own, and offers none of the things a link offers.
Also common: the empty href
<a href="#"> used as a button is a link that goes nowhere, and the list of what breaks is longer than the semantics. It is announced as a link and appears in the links list. Activating it can jump the page to the top. Copying it, dragging it, and bookmarking it all produce something meaningless. And while JavaScript is still loading, or if it errors, or if it is disabled, the control does literally nothing. Note that an <a> with no href at all is a different failure. That one is not focusable and carries no link role, so it fails like the bare div rather than like the fake link, which matters when you are diagnosing rather than describing.
The Edge Cases
- A link that opens a modal. A button. It does not go anywhere.
- A button that navigates after saving. Still a button. The navigation is a consequence, not the purpose.
- Pagination. Links, if each page has its own URL.
- Tabs. Buttons, inside a composite widget with its own arrow key handling.
- Skip to content. A link. It moves you within the page.
- A logo going home. A link.
- Download. A link, since it points at a file. Say so in the link text as well, because a new tab or a download that arrives unannounced is disorienting for anybody with low vision or a cognitive difficulty, and older screen readers may not announce it at all. The same goes for anything using
target="_blank". - A toggle. A button, and it needs
aria-pressedto say which state it is in. One rule goes with that. The label must not change when the state does. Mute stays Mute, andaria-pressedcarries whether it is on. If the design calls for the label to flip between Mute and Unmute, then leavearia-pressedoff entirely, because the label is doing the job. Doing both announces the state twice in two different words.
Styling a Link Like a Button
It is fine, it is very common, and it is worth knowing that W3C's own position is a step further than fine. Its guidance on the reverse case, a button styled as a link, is that giving the element the right role helps, and that the better solution is to adjust the visual design so it matches the function and the role. The principle underneath applies to both directions. Both the appearance and the role of a control should match the function it provides.
So style your primary call to action as a button by all means. Just do not let the styling drift so far that people press Space on it and the page scrolls instead.
One honest limit
No scanner can tell you this is wrong. The markup is valid either way, and the element carries no clue about whether it navigates. There is evidence for saying that rather than only an opinion. Every automated rule written against the name, role and value criterion tests whether a name exists rather than whether it is right, and none of them distinguishes a link from a button by what it does. Only a person reading the page can. Our automated versus manual guide covers where that line sits.