Tooltips are a nice way to show the user extra info about the site's UI. By making use of CSS attribute selectors and the attr()
function, we're able to create a neat little tooltip to show our users extra info that we don't want to add to the element's text. Scroll down to the bottom to see the tooltips in action, or follow along to learn how to make them yourself!
Preface
Before we begin, we need to understand what problems tooltips solve. They help confused users understand the UI we have created. Confused users come in all different shapes and we need to help them all. This means our tooltips must work for
- Sighted users
- Users with vision impairments
- Those using a mouse
- Those who prefer navigating the web with only a keyboard
Descriptive text can be added through the use of aria-*
attributes, such as aria-label
and aria-description
, and the keyboard users need the element to be focusable. This pretty much sorts itself out, as any element with a tooltip probably is some kind of a focusable element already (a
, button
, input
, textarea
), however should you need to add a tooltip to a div
, please think twice to help those of us who won't be able to see that information.
We also need to add some kind of hover effect for those of us using a mouse pointer. Is there a way to neatly combine all these things such that our tooltips make a great developer experience as well? Let's write some code!
Step 1: The HTML
There is not much code to write in the HTML part of this, which is actually great, as it means it won't become a hassle to use it!
Let's start with a simple button:
<button
data-tooltip
aria-description="Stormy weather ahead!">
⚡⚡⚡
</button>
The aria-description
attribute makes screen readers read both the text inside the button
element, as well as the description text. We'll use the data-tooltip
to tell the browser that this element should have a tooltip.
Step 2: Creating a tooltip in CSS
By using pseudo-elements and content: attr()
, we can display any of the element's attribute values:
/* Target any element with the `data-tooltip` and `aria-description` attributes */
[data-tooltip][aria-description]::before {
content: attr(aria-description);
display: none;
}
[data-tooltip][aria-description]:hover::before,
[data-tooltip][aria-description]:focus::before {
/* Show the tooltip on hover and focus */
display: block;
}
Step 3: Styling the tooltip
Ok, now we have a default styled button and some text that pops out whenever we hover or set focus to it. The description can even be read by screen readers! We've done great already, but the UX is not very good for sighted users. Let's fix that!
Check out the final result on CodePen:
Top comments (1)
Thanks for the tutorial. It's been a couple of years since you posted it. Would you make any adjustments to the code today? Although the code works great, I get the following vscode error, "ARIA attributes must conform to valid names: Invalid ARIA attribute name: aria-description". To avoid this error, I replaced content: attr(aria-description) with content: attr(data-tooltip) so that the data-tooltip content will now display instead--for example, data-tooltip="contact us" data-tooltip-position="bottom".
I changed your css to the following:
data-tooltip {
position: relative;
--tooltip-bg: #000;
--tooltip-color: #fff;
--tooltip-font-size: .8em;
--tooltip-font-weight: 600;
}
data-tooltip::after {
left: 50%;
opacity: 0;
position: absolute;
top: calc(100% + 0.5rem);
transition: opacity 0.15s ease-in-out, visibility 0s 0.15s ease-in-out;
visibility: hidden;
}
data-tooltip::before {
border-bottom: 8px solid var(--tooltip-bg, #000);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
content: "";
height: 0;
transform: translateX(-50%) translateY(calc(-100% + 1px));
width: 0;
z-index: 1;
}
data-tooltip::after {
background-color: var(--tooltip-bg, #000);
border-radius: 5px;
color: var(--tooltip-color, #fff);
content: attr(data-tooltip);
font-size: var(--tooltip-font-size, 0.8rem);
font-weight: var(--tooltip-font-weight, 600);
padding: 0.5em 0.75em;
transform: translate3d(-50%, 0, 0);
white-space: nowrap;
z-index: 1;
}
data-tooltip:focus::after {
opacity: 1;
transition: opacity 0.15s ease-in-out;
visibility: visible;
}
[data-tooltip][data-tooltip-position=top]::before,
[data-tooltip][data-tooltip-position=top]::after {
bottom: calc(100% + 0.5rem);
top: auto;
}
[data-tooltip][data-tooltip-position=top]::before {
transform: translateX(-50%) translateY(calc(100% - 1px)) rotateZ(180deg);
}
[data-tooltip][data-tooltip-position=left]::before,
[data-tooltip][data-tooltip-position=left]::after {
left: 0;
top: 50%;
}
[data-tooltip][data-tooltip-position=left]::before {
transform: translateX(calc(-100% - 1px)) translateY(-50%) rotateZ(90deg);
}
[data-tooltip][data-tooltip-position=left]::after {
transform: translateX(calc(-100% - 0.5rem + 4px)) translateY(-50%);
}
[data-tooltip][data-tooltip-position=right]::before,
[data-tooltip][data-tooltip-position=right]::after {
left: auto;
right: 0;
top: 50%;
}
[data-tooltip][data-tooltip-position=right]::before {
transform: translateX(calc(100% + 5px)) translateY(-50%) rotateZ(270deg);
}
[data-tooltip][data-tooltip-position=right]::after {
transform: translateX(calc(100% + 0.5rem)) translateY(-50%);
}
@media (hover: none) {
}