Tooltips and Toggletips
Applicable WCAG 2.1 Success Criteria
A tooltip provides extra information about a form field, a link, a button, or other focusable element. It must be triggered by both focus and hover events and remains on the screen as long as the trigger has the focus. The focus does not move to the tooltip.
Functionality
So, what functionality do we want from a tooltip?
- Tooltip should appear when an interactive element is hovered over with mouse, AND when it recieves keyboard focus.
- Screen readers will read the interactive element itself, then the tooltip text.
- The tooltip shoould be hidden and shown using JavaScript and CSS.
- Tooltips can be used as a primary label for interactive elemtns, or to provide an alternate desciption.
In this example, we’re going to use the tooltip text as an accessible label. The tooltip trigger itself will be a <button>
. When this button is hovered over with the mouse or tabbed to using the keyboard, the tooltip text will be read aloud to a screen reader. We’ll use CSS to hide / show the tooltip on hover and focus, and the aria-labelledby
attribute to link the tooltip text to the trigger button.
Demo
View help and manage settings
Code Pattern
<button class="notifications" aria-labelledby="tooltip-label">
<!-- Your SVG, IMG, icon here! -->
</button>
<div class="arrow_box" role="tooltip" id="tooltip-label">Hi, I'm tooltip text! Hopefully, something useful and brief.</div>
[role="tooltip"] {
display: none;
border: 2px solid black;
padding: 10px;
border-radius: 5px;
width: 40%;
}
.arrow_box {
position: relative;
background: #fff;
border: 2px solid #000;
margin-top: 15px;
}
.arrow_box:after, .arrow_box:before {
bottom: 100%;
left: 11%;
border: solid transparent;
content: "";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.arrow_box:after {
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #fff;
border-width: 20px;
margin-left: -20px;
}
.arrow_box:before {
border-color: rgba(0, 0, 0, 0);
border-bottom-color: #000;
border-width: 23px;
margin-left: -23px;
}
button:hover + [role="tooltip"],
button:focus + [role="tooltip"] {
display: block;
}
button {
font-size: 1.25rem;
border-radius: 0.33em;
font-family: inherit;
width: 120px;
height: 120px;
color: #fefefe;
padding: 0.75rem;
border: 0;
background: #fff;
}
svg {
width: 100%;
}
Demo
Code Pattern
<span class="toggletip-container">
<button type="button" aria-label="more info" data-toggletip-content="Hi! I'm the toggletip text.">i</button>
<span role="status"></span>
</span>
.toggletip-container {
position: relative;
display: inline-block;
}
/* the bubble element, added inside the toggletip live region */
.toggletip-bubble {
display: inline-block;
position: absolute;
left: 100%;
top: 0;
width: 10em;
padding: 0.5rem;
background: #000;
color: #fff;
}
button {
width: 2em;
height: 2em;
border-radius: 50%;
border: 0;
background: #000;
font-family: serif;
font-weight: bold;
color: #fff;
}
button:focus {
outline: none;
box-shadow: 0 0 0 0.25rem skyBlue;
}
/* boilerplate; nothing really to see here */
html {
font-size: 150%;
font-family: sans-serif;
}
* {
font-size: inherit;
}
(function() {
// Get all the toggletip buttons
var toggletips = demo.querySelectorAll('[data-toggletip-content]');
// Iterate over them
Array.prototype.forEach.call(toggletips, function (toggletip) {
// Get the message from the data-content element
var message = toggletip.getAttribute('data-toggletip-content');
// Get the live region element
var liveRegion = toggletip.nextElementSibling;
// Toggle the message
toggletip.addEventListener('click', function () {
liveRegion.innerHTML = '';
window.setTimeout(function() {
liveRegion.innerHTML = '<span class="toggletip-bubble">'+ message +'</span>';
}, 100);
});
// Close on outside click
demo.addEventListener('click', function (e) {
if (toggletip !== e.target) {
liveRegion.innerHTML = '';
}
});
// Remove toggletip on ESC
toggletip.addEventListener('keydown', function (e) {
if ((e.keyCode || e.which) === 27)
liveRegion.innerHTML = '';
});
// Remove on blur
toggletip.addEventListener('blur', function (e) {
liveRegion.innerHTML = '';
});
});
}());
Tested using
|
Firefox
with NVDA
|
Chrome
|
Safari iOS
with Voiceover
|
Edge
|
References