Minimal Accessible Tooltip
Doing a lot of web accessibility fixes, I wondered what would be the minimum amount of code to create a WCAG 2.1 compliant custom tooltip. Unfortunately, it had to include Javascript. Here is what I came up with.
Introduction
Technically, the minimal WCAG 2.1 compliant tooltip is to use the inbuilt browser tooltip you get when you set the title
attribute on HTML elements. However, they normally do not meet the criteria in Success Critera 1.4.13 — they are not dismissable or hoverable. This means that you can't hide them with, for instance, the escape key, and magnifier users will not be able to hover over the tooltip to read it.
I was intrigued to find out what was the minimum markup/code required to get a custom tooltip that was fully WCAG 2.1 compliant.
HTML and CSS
I started with just HTML and CSS. Unfortunately, though you can make a hoverable and persistent tooltip easily, it is not dismissable. So close…
The code below shows the minimum required HTML and CSS for creating the almost compliant tooltip. I have also put a demo of the code on CodePen.
It uses the tooltipped
class to distinguish elements that have a tooltip and the role="tooltip"
global attribute to distinguish the tooltip itself. Note the tooltip is contained within the element that has to tooltip. Also, as I have used a div for the tooltipped element, I have put a tabindex="0"
attribute on the div so that it is focussable.
HTML and CSS for creating an almost compliant tooltip
<html>
<head>
<style>
.tooltipped {
display: inline-block;
}
.tooltipped [role=tooltip] {
display: none;
}
.tooltipped:focus-within [role=tooltip],
.tooltipped:hover [role=tooltip] {
display: block;
position: absolute;
}
</style>
</head>
<body>
<div class="tooltipped" tabindex="0">
Has tooltip
<div role="tooltip">
A tooltip
</div>
</div>
</body>
</html>
HTML, CSS and Javascript
The only way I could think of to do it, was to additionally use Javascript to handle the events around the tooltip.
Below is the minimum code required to get a fully compliant tooltips. I have also put a demo of the code on CodePen.
Instead of using CSS for showing and hiding the tooltip, Javascript attaches event listeners onto all the page tooltips to handle the opening and closing of the tooltips. An additional keyup
event listener is used to close the open tooltip if the escape key is pressed. I added a slight tweak on the mouseleave
event so that if the element also has keyboard focus, the tooltip won't be closed when the mouse leaves the element.
HTML, CSS and Javascript for creating a fully compliant tooltip
<html>
<head>
<style>
[role=tooltip] {
display: block;
position: absolute;
background: #eee;
padding: 5px;
}
</style>
<script>
/**
* Add event listeners for handling tooltip functionality
*/
const initialiseTooltipped = () => {
const tooltips = document.querySelectorAll('.tooltipped');
let shownTooltip = null;
// Add event handler for closing open tooltip on escape key
document.addEventListener('keyup', (event) => {
if (event.key !== 'Escape') {
return;
}
if (shownTooltip) {
shownTooltip.style.display = 'none';
shownTooltip = null;
}
})
for (let tooltipped of tooltips) {
const tooltip = tooltipped.querySelector('[role=tooltip]');
if (!tooltip) {
return;
}
/**
* Show/hide the associated tooltip
*
* @param {boolean} [show] Whether to show to tooltip.
* Defaults to true
*/
const showTooltip = (show) => {
if (show === undefined) {
show = true;
}
if (show) {
tooltip.style.display = '';
shownTooltip = tooltip;
} else {
tooltip.style.display = 'none';
shownTooltip = null;
}
};
// Add handlers to tooltipped element
tooltipped.addEventListener('focus', () => showTooltip(true));
tooltipped.addEventListener('blur', () => showTooltip(false));
tooltipped.addEventListener('mouseenter', () => showTooltip(true));
tooltipped.addEventListener('mouseleave', () => {
if (document.activeElement !== tooltipped) {
showTooltip(false)
}
});
}
}
// Call initialiseTooltipped once the DOM has loaded
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', initialiseTooltipped);
} else {
initialiseTooltipped();
}
</script>
</head>
<body>
<div class="tooltipped" tabindex="0">
Has tooltip
<div role="tooltip" style="display: none">
A tooltip
</div>
</div>
</body>
</html>