
Building Accessible Tooltips with aria-describedby and Keyboard Triggers
Tooltips are frequently mishandled on the web. Naive implementations rely exclusively on the browser's native title attribute (which fails on touch devices, lacks styling, and has erratic screen reader timing) or build custom <div> overlays that only trigger on mouse hover, completely stranding keyboard users.
Creating an accessible tooltip requires semantic markup, explicit focus triggers, and adherence to WCAG 2.1 Success Criterion 1.4.13 (Content on Hover or Focus).
Example
<button type="button" class="btn-action" aria-describedby="tip-save">
Save Changes
</button>
<div id="tip-save" role="tooltip" class="tooltip-bubble" hidden>
Autosaves to cloud every 60 seconds
</div>
Implementation
The tooltip must display when the triggering button is hovered OR focused via keyboard, and dismiss when the user presses Escape:
.btn-action {
position: relative;
}
.tooltip-bubble {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: var(--darkgray);
color: #ffffff;
padding: 6px 12px;
border-radius: 4px;
font-size: 13px;
white-space: nowrap;
pointer-events: none;
z-index: 1000;
}
/* Reveal tooltip on button focus or hover */
.btn-action:hover + .tooltip-bubble,
.btn-action:focus + .tooltip-bubble {
display: block;
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('[role="tooltip"]').forEach((tip) => {
tip.hidden = true;
});
}
});
Main Point: Link tooltips to interactive controls with aria-describedby, reveal them on both :focus and :hover, and allow dismissal via the Escape key.
Credits
Photo by Sean Oulashin on Unsplash