CSS-only Tooltip
Build elegant, accessible tooltips using only CSS pseudo-elements, the attr() function, and smooth transitions.
CSS-only Tooltip
Tooltips provide extra information when a user hovers over an element. In this episode you will build fully functional tooltips using only CSS — no JavaScript, no tooltip libraries. We will use pseudo-elements, the attr() function, and CSS transitions.
The HTML — Using data Attributes
<button class="tooltip" data-tooltip="Save your progress">
Save
</button>
<a href="#" class="tooltip" data-tooltip="Go back to the homepage">
Home
</a>
<span class="tooltip" data-tooltip="This field is required">
Username *
</span>
The tooltip text is stored in a data-tooltip attribute. Any element with the class .tooltip and a data-tooltip attribute will show a tooltip on hover.
The Tooltip Bubble — ::after Pseudo-element
.tooltip {
position: relative;
cursor: pointer;
}
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
padding: 6px 12px;
background: #333;
color: #fff;
font-size: 13px;
white-space: nowrap;
border-radius: 4px;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
transform: translateX(-50%) translateY(4px);
}
The content: attr(data-tooltip) pulls the tooltip text directly from the HTML attribute. The tooltip is positioned above the element using bottom: calc(100% + 8px) with an 8px gap. It starts invisible (opacity: 0) and slightly shifted down (translateY(4px)) for a slide-up entrance effect.
The Arrow — ::before Pseudo-element
.tooltip::before {
content: "";
position: absolute;
bottom: calc(100% + 2px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
}
The arrow is a CSS triangle created with the border trick. A 6px transparent border on all sides, with only the top border colored, creates a small downward-pointing triangle that connects the tooltip bubble to the element below it.
Showing the Tooltip on Hover
.tooltip:hover::after {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.tooltip:hover::before {
opacity: 1;
}
On hover, both the bubble and arrow fade in. The bubble also slides up to its final position (translateY(0)), creating a smooth entrance animation.
Tooltip Positions
The default tooltip appears above. Here is how to create bottom, left, and right variants:
Bottom Tooltip
.tooltip-bottom::after {
bottom: auto;
top: calc(100% + 8px);
transform: translateX(-50%) translateY(-4px);
}
.tooltip-bottom::before {
bottom: auto;
top: calc(100% + 2px);
border-top-color: transparent;
border-bottom-color: #333;
}
.tooltip-bottom:hover::after {
transform: translateX(-50%) translateY(0);
}
Left Tooltip
.tooltip-left::after {
bottom: auto;
left: auto;
right: calc(100% + 8px);
top: 50%;
transform: translateY(-50%) translateX(4px);
}
.tooltip-left::before {
bottom: auto;
left: auto;
right: calc(100% + 2px);
top: 50%;
transform: translateY(-50%);
border-top-color: transparent;
border-right-color: transparent;
border-left-color: #333;
}
.tooltip-left:hover::after {
transform: translateY(-50%) translateX(0);
}
Styling Variations
/* Dark theme */
.tooltip-dark::after {
background: #1a1a2e;
color: #e0e0e0;
}
/* Success theme */
.tooltip-success::after {
background: #27ae60;
}
.tooltip-success::before {
border-top-color: #27ae60;
}
/* Warning theme */
.tooltip-warning::after {
background: #f39c12;
color: #333;
}
.tooltip-warning::before {
border-top-color: #f39c12;
}
Accessibility Consideration
.tooltip:focus::after,
.tooltip:focus::before {
opacity: 1;
}
.tooltip:focus::after {
transform: translateX(-50%) translateY(0);
}
Adding :focus alongside :hover ensures keyboard users can also see the tooltip when they tab to the element.
Key Takeaways
content: attr(data-tooltip)pulls tooltip text from an HTML data attribute into a pseudo-element — no JavaScript needed- The
::afterpseudo-element creates the tooltip bubble, and::beforecreates the arrow - CSS triangles are made with the border trick — transparent borders on three sides, colored on one side
- Combining
opacityandtransformtransitions creates a smooth fade + slide entrance effect pointer-events: noneprevents the tooltip from interfering with hover states on elements below it- Add
:focusstyles alongside:hoverfor keyboard accessibility