You will build a CSS-only tooltip that shows contextual text when a user hovers or focuses an element, using a data attribute and a pseudo-element.
What it is
A tooltip is a small floating label that appears near an element to explain it. In CSS, the common mental model is: store the tooltip text in a data-tip attribute, create a pseudo-element with ::after, position it absolutely, and reveal it on :hover or :focus-visible. Related terms include generated content, attribute selector, positioned ancestor, and pointer events.
Why it matters
- It gives users a quick explanation without cluttering the interface.
- It can be built with CSS only, so it works even when JavaScript is unavailable.
- It supports keyboard users when focus styles are included.
- It keeps tooltip text close to the element it describes.
Syntax or steps
- Add a
data-tipattribute to the element that needs a tooltip. - Give the element
position: relativeso the tooltip can be positioned against it. - Create
.tip::afterand setcontent: attr(data-tip). - Position the pseudo-element with
position: absolute, then hide it withopacity: 0. - Show it with
.tip:hover::afterand.tip:focus-visible::after.
Example
<button class="tip" data-tip="Save your work">Save</button>
.tip {
position: relative;
display: inline-block;
}
.tip::after {
content: attr(data-tip);
position: absolute;
left: 50%;
bottom: 100%;
transform: translate(-50%, -8px);
padding: 6px 10px;
background: #222;
color: #fff;
border-radius: 4px;
white-space: nowrap;
font-size: 0.875rem;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
z-index: 10;
}
.tip:hover::after,
.tip:focus-visible::after {
opacity: 1;
}
The button is the trigger. The data-tip attribute stores the visible text. The .tip rule creates a positioning context. The .tip::after rule builds the tooltip box, centers it above the button, and keeps it invisible until hover or keyboard focus. pointer-events: none prevents the tooltip from stealing the hover state, and z-index helps it appear above nearby content.
Common mistakes
- Forgetting
position: relativeon the trigger. The tooltip may attach to the wrong ancestor. Addposition: relativeto the element with.tip. - Letting the tooltip be clipped. If an ancestor has
overflow: hidden, the tooltip can disappear. Move the tooltip outside that container or avoid clipping. - Using only
:hover. Keyboard users cannot see the tooltip. Add:focus-visibleand make sure the trigger can receive focus. - Putting essential information only in CSS. Generated content may not be announced reliably. If the text is important, provide it in the accessible name or description.
When to use it
CSS tooltips are best for short, non-critical hints. Compare them with the native title attribute and JavaScript tooltips:
| Approach | Best for | Limitations |
|---|---|---|
| CSS tooltip | Styled hints on hover or focus | Needs careful positioning and accessibility |
title attribute | Simple native tooltips | Hard to style, unreliable on touch and keyboard |
| JavaScript tooltip | Dynamic content, touch support, complex positioning | More code and state management |
Practice
- Guided exercise: Add a tooltip to a button that says
Delete permanently. Expected output: the tooltip appears above the button when hovered or focused. - Challenge: Create a
.tip-bottomvariant that appears below the trigger. Hint: changebottom: 100%totop: 100%and adjust thetransformvalue.
Quick check
Why is pointer-events: none useful on a CSS tooltip?
It stops the tooltip from intercepting the mouse, which can otherwise cause flickering when the pointer moves over the tooltip.
Summary
A CSS tooltip combines a data attribute, a pseudo-element, and absolute positioning. The transferable idea is to separate content from presentation: the trigger stores the text, while CSS controls when and where it appears.