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
- CSS Tricks - When To Use The Button Element
- Adam Silver - Buttons shouldn’t have a hand cursor
- MDN - ARIA: button role
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 Examplebefore
after
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.
source
Comparing /examples/semantics/buttons-and-links/before/index.html to /examples/semantics/buttons-and-links/after/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<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" />
</head>
<body>
<h1>Buttons and Links examples</h1>
<h2>Implicit Semantic Meaning</h2>
<p>
We lose implicit semantic meaning when using a tag other than
<code><a></code> or <code><button></code> for interactive
controls.
</p>
<spabutton class="btn span-btn">This is aspan pretending to be abutton</spabutton>
<h2>Keyboard Triggering</h2>
<p>
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.
</p>
<a href="#anchor" class="btn link-btn" role="button"
>This is a link with a button role</a
>
<h2>Pointer Cursor</h2>
<p>
The pointer cursor shouldn't be on anything but links, because of their
weak affordance.
<sup
>[<a href="https://www.w3.org/TR/CSS21/ui.html#propdef-cursor">1</a
>]</sup
>
</p>
<button class="btn pointer-btn">This is a button, not a link</button>
<h2>Last Resort</h2>
<p>
There are times when you absolutely can't use <code><a></code> or
<code><button></code>, in these cases you must provide the semantic
meaning and functionality.
</p>
<div class="btn div-btn" role="button" tabindex="0">
This is a div pretending to be a button
</div>
<script src="buttons-and-links.js"></script>
</body>
</html>
styles
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;
}
javascript
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') {
e.target.click();
}
}
})();