Buttons & Links

It's very easy to lose implicit semantic meaning of buttons and links, or to forget to provide it for other elements that need to be interactive.

The Problem

Expected keyboard controls and screen reader output are omitted without proper semantics.

The Solution

Ensure semantic meaning is provided in cases where it is not implicit. Four examples below.

Related Articles

Live Examples

Before illustrates the problem, After illustrates the solution. Click the header to see it larger in a modal.

Please note that this Before example contains inaccessible code, use this link to skip to the After Live Example



Code Comparison

Code diff between the before and after examples above to show the changes necessary. To copy the final source click on the 'after' path link before the diff.


Comparing /examples/semantics/buttons-and-links/before/index.html to /examples/semantics/buttons-and-links/after/index.html

<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Accessibility Solutions - Semantics - Buttons and Links</title>
<link rel="stylesheet" href="../../../presentation.css" />
<link rel="stylesheet" href="buttons-and-links.css" />
<h1>Buttons and Links examples</h1>
<h2>Implicit Semantic Meaning</h2>
We lose implicit semantic meaning when using a tag other than
<code>&lt;a&gt;</code> or <code>&lt;button&gt;</code> for interactive
spabutton class="btn span-btn">This is a span pretending to be a button</spabutton>

<h2>Keyboard Triggering</h2>
Links are triggered with the Enter key, buttons are triggered with the
Space or Enter key. Links with a button role should account for that.
<a href="#anchor" class="btn link-btn" role="button"
>This is a link with a button role</a

<h2>Pointer Cursor</h2>
The pointer cursor shouldn't be on anything but links, because of their
weak affordance.
>[<a href="https://www.w3.org/TR/CSS21/ui.html#propdef-cursor">1</a
<button class="btn pointer-btn">This is a button, not a link</button>

<h2>Last Resort</h2>
There are times when you absolutely can't use <code>&lt;a&gt;</code> or
<code>&lt;button&gt;</code>, in these cases you must provide the semantic
meaning and functionality.
<div class="btn div-btn"
role="button" tabindex="0">
This is a div pretending to be a button

<script src="buttons-and-links.js"></script>


Comparing /examples/semantics/buttons-and-links/before/buttons-and-links.css to /examples/semantics/buttons-and-links/after/buttons-and-links.css

.btn {
border: 1px solid #333;
border-radius: 5px;
color: #333;
cursor: pointer;
font-size: 16px;
font-weight: normal;
padding: 8px;

.div-btn {
display: inline-block;

.link-btn {
color: #333;
text-decoration: none;

.link-btn:visited {
color: #333;

.span-btn {

cursor: default;

display: inline-block;


Comparing /examples/semantics/buttons-and-links/before/buttons-and-links.js to /examples/semantics/buttons-and-links/after/buttons-and-links.js

(function () {
'use strict';

const divBtn = document.querySelector('.div-btn');
const linkBtn = document.querySelector('.link-btn');
const pointerBtn = document.querySelector('.pointer-btn');
const spanBtn = document.querySelector('.span-btn');

// Set up example event listeners and click events
divBtn.onclick = clickEvent;
linkBtn.onclick = clickEvent;
linkBtn.addEventListener('keydown', activateEvent);
pointerBtn.onclick = clickEvent;
spanBtn.onclick = clickEvent;
spanBtn.addEventListener('keydown', activateEvent);

function clickEvent() {
alert('Hey, you clicked a button-like element!');


function activateEvent(e) {
if (e.key === 'Enter' || e.code === 'Space') {