Building a tooltip with only CSS

Following Jesus; Husband; Father; Developer; Gamer; Tinkerer; Writing about code
Search for a command to run...

Following Jesus; Husband; Father; Developer; Gamer; Tinkerer; Writing about code
No comments yet. Be the first to comment.
Today ChatGPT lied to me, so I’m setting the record straight. This is my question: “If an endpoint in an OpenAPI spec has multiple tags, what will Swagger Codegen do?”. Because Swagger Codegen organized the generated code into api/tag_name_api files....

A Short Story

Universal Libraries, The Internet, and Generative AI

This is the article I wished existed when I needed to support accounting functions. Double-entry Bookkeeping may seem esoteric and unnecessarily complicated, but actually describes a simple framework for maintaining a correctible and auditable record...

How to Change Databases without Downtime

On this page
There's a plenty of libraries (mostly JavaScript) to enable the classic tooltip design pattern. But let's build our own! And let's not pollute our HTML with unnecessary markup and avoid loading JavaScript while we're at it.
Attribute selector
a. [attrubute] - selects an element with attribute (regardless of attribute's value)
b. [attribute^="prefix"] - selects an element with attribute where the value starts with prefix
c. [attribute$="postfix"] - selects an element with attribute where the value ends with postfix
Psuedo-class :hover - selects an element that has a mouse cursor hovering over it
Psuedo-elements
a. ::after - styleable element that doesn't actually exist in the DOM; considered the last child of the selected element
b. ::before - styleable element that doesn't actually exist in the DOM; considered the first child of the selected element
content - used in ::before and ::after psuedo-elements to set their content
attr("attribute") - accepts an argument attribute, and can be used to set the content of a psuedo-element based on the value of said attribute on the element selected
position (along with top, left, right, bottom, and transform) - Used to position the tooltip
calc() - accepts a basic mathematical expression and calculates the final value (what's useful to us is how we can mix units, like width: calc(40px + 50%) which will give us the width to 50% of what it could be plus a constant 40 pixels extra)
On any HTML tag (span, a, div, ...) let's add two attributes: one to describe which direction we want the tooltip to appear in and one to specify the content of the tooltip. Let's use aria-label to contain the content and data-tooltip for the tooltip configuration.
<any-tag aria-label="Tooltip Content" data-tooltip="up" >Normal Content</any-tag>
The tooltip will need a couple things:
aria-labelTo select our element we can use the attribute selector (at this point it doesn't matter what the content is), let's select elements that have both of our tooltip attributes [data-tooltip][aria-label].
On the element itself, we need a position: relative. This is so when we go to position: absolute the psuedo-element that is our tooltip it will position itself based on the element and not some other parent.
Finally, to hide the tooltip, let's put both opacity: 0 and visibility: none. (Hiding can also be done with display: none, but that cannot be transitioned nicely the way opacity + visibility can)
[data-tooltip][aria-label] {
position: relative;
}
[data-tooltip][aria-label]::before {
content: attr(aria-label);
position: absolute;
opacity: 0;
visibility: none;
}
note that the codepen above has extra styles for color and spacing, but these are the important parts for making the tooltip work
Now we should select based on particular configuration in the data-tooltip attribute. We can select where the value starts with something by [data-tooltip^="startswiththisvalue"] (we'll select by the starting value instead of exact value to leave room in the attribute for configuring other things later). We want to do two things for the Up/Down direction of tooltips:
Placement above is simple enough: we will position ourselves from the bottom of the element, 100% (that is, place the bottom of the tooltip 100% of the elements height away from the bottom of the element), and add the small margin using calc(). And for below, the same principle applies, but we use top instead of bottom.
[data-tooltip^="up"][aria-label]::before {
bottom: calc(0.5rem + 100%);
}
[data-tooltip^="down"][aria-label]::before {
top: calc(0.5rem + 100%);
}
Centering the tooltip can be thought of as two distinct steps:
And we will do just that with two styles: right: 50% (which places the right edge of the tooltip in the center of the element) and transform: translateX(50%) which moves the tooltip from that position to the right by 50% of its own width. We'll add this to both Up/Down:
[data-tooltip^="up"][aria-label]::before {
bottom: calc(0.5rem + 100%);
right: 50%;
transform: translateX(50%);
}
[data-tooltip^="down"][aria-label]::before {
top: calc(0.5rem + 100%);
right: 50%;
transform: translateX(50%);
}
Positioning left and right uses the same concepts as above, but instead of centering vertically we want to center horizontally and instead of positioning at the top and bottom of the element we want to position at the left and right.
[data-tooltip^="left"][aria-label]::before {
right: calc(1rem + 100%);
bottom: 50%;
transform: translateY(50%);
}
[data-tooltip^="right"][aria-label]::before {
left: calc(1rem + 100%);
bottom: 50%;
transform: translateY(50%);
}
The last new piece of CSS we need is the Psuedo-class :hover. As the name implies, this will select an element that has a pointer hovering over it (just when we want to display the tooltip!). And all we need to do is change it from the default invisible style we gave it above to a visible style:
[data-tooltip][aria-label]:hover::before {
visibility: visible;
opacity: 1;
}
And we now have a totally functional (if a little ugly) CSS-only tooltip! You can stop here or keep going to add some niceties you would expect of a tooltip: delay, gentle transition, triangular-tab...
We can use the transition-* styles to delay when the tooltip appears and gently fade in:
[data-tooltip][aria-label]:hover::before {
visibility: visible;
opacity: 1;
transition-property: opacity;
transition-duration: 0.2s;
transition-timing-function: ease-in-out;
transition-delay: 0.5s;
}
transitioning on the opacity gives a nice fade in effect. A duration of 0.2 seconds should make it quick enough to avoid being annoying. And we can set a delay to not immediately show the tooltip (avoids popping information in and out while users move their mouse across the page; also referred to as "hover intent").
We can do even better! Let's allow the element to configure how long the delay (if any) the tooltip should have. Unfortunately we can't dynamically allow any value in transition-delay (attr() only works in the content style) so we will have to select a few predetermined delay times.
[data-tooltip][aria-label]:hover::before {
visibility: visible;
opacity: 1;
transition-property: opacity;
transition-duration: 0.2s;
transition-timing-function: ease-in-out;
transition-delay: 0s;
}
[data-tooltip$="500"][aria-label]:hover::before {
transition-delay: 0.5s;
}
Here I've used the ending portion ($=) of the data-tooltip attribute to set the delay (in milliseconds). I hope it is clear how this would be extended to add delays of 0.1 seconds up to 1 second and beyond.
I will actually leave this as an exercise to you, dear reader. (Or you'll just copy from the codepen I guess...). But I will give you hints:
::before, thankfully there's also ::after...borders on a no-width, no-height element. Think about what it would look like if you made thick borders around that element and gave each one a different color (also remember that "color" can mean transparent)
Thanks for reading this far! I hope you learned something or at least enjoyed watching me battle the CSS dragon.
I like sharing and finding things like this where common web design patterns can be done without a massive JavaScript library or framework bogging the page down. Give me some suggestions below on patterns you'd like to see me break-down in CSS/HTML-only and maybe this can become a series.
A side note on selection of aria-label. This is fine and keeps things relatively accessible, but the better option is title. This is the most native "tooltip" there is. However, there's no styling options available (even just disabling the default effect, as we could then disable and do what we've done here to make the tooltip). Since we can't disable the default effect, it would make our tooltip appear twice (once the way we styled it to be, and once in the default way).