How to Create a Tooltip in CSS
Build an accessible tooltip that displays a short helper message next to an element on hover and on keyboard focus.
What a tooltip is for
A tooltip is a small floating label that appears next to an element to explain what it does, without permanently taking up space on the page. Icon-only buttons, abbreviations, and truncated text are common places to add one, since the visible label alone doesn't tell the whole story. The cleanest way to build a pure-CSS tooltip is to wrap the trigger element in a container, give that container `position: relative`, and attach the tooltip bubble as a `::after` pseudo-element with `position: absolute` - hidden by default and revealed with `:hover` and `:focus` on the container.
Example: CSS-only tooltip
<!DOCTYPE html>
<html>
<head>
<style>
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted #666;
cursor: help;
}
.tooltip::after {
content: attr(data-tooltip); /* pulls the text from the HTML attribute */
position: absolute;
bottom: 125%;
left: 50%;
transform: translateX(-50%) translateY(4px);
background: #222;
color: #fff;
padding: 6px 10px;
border-radius: 4px;
font-size: 0.8rem;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease, transform 0.15s ease;
pointer-events: none;
}
/* small triangle pointing down at the trigger */
.tooltip::before {
content: "";
position: absolute;
bottom: 118%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: #222;
opacity: 0;
visibility: hidden;
transition: opacity 0.15s ease;
}
.tooltip:hover::after,
.tooltip:hover::before,
.tooltip:focus::after,
.tooltip:focus::before {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
</style>
</head>
<body>
<button class="tooltip" data-tooltip="Copies the link to your clipboard">
Copy link
</button>
</body>
</html>`content: attr(data-tooltip)` is the key line: it lets the CSS pull its text straight from a `data-tooltip` attribute on the HTML element, so you can reuse the same tooltip styling for any trigger just by changing that attribute - no duplicated CSS rules per tooltip. Always keep the `:focus` selector alongside `:hover` in the trigger rule: a mouse user can hover to see the tooltip, but a keyboard user tabbing through the page can only trigger `:focus`, so without it keyboard users never see the tooltip at all.
Positioning the tooltip on any side
The example above places the bubble above the trigger. To point it in another direction, change which sides you position from: use `top: 125%` instead of `bottom: 125%` to place it below, or swap `left`/`right` and adjust the arrow's border-color to point sideways.
- Above trigger: position the bubble with bottom, point the arrow's border-top-color.
- Below trigger: position the bubble with top, point the arrow's border-bottom-color.
- Keep pointer-events: none on the bubble so it never blocks clicks on nearby elements.
When to reach for JavaScript instead
Example: repositioning a shared tooltip with JavaScript
<!DOCTYPE html>
<html>
<head>
<style>
.js-tooltip {
position: fixed;
background: #222;
color: #fff;
padding: 6px 10px;
border-radius: 4px;
font-size: 0.8rem;
pointer-events: none;
}
</style>
</head>
<body>
<div id="js-tooltip" class="js-tooltip" hidden></div>
<button class="js-tip-trigger" data-tip="Downloads as a .zip file">Download</button>
<script>
var tip = document.getElementById('js-tooltip');
document.querySelectorAll('.js-tip-trigger').forEach(function (trigger) {
function showTip() {
var rect = trigger.getBoundingClientRect();
tip.textContent = trigger.dataset.tip;
tip.hidden = false;
tip.style.left = rect.left + 'px';
tip.style.top = (rect.top - tip.offsetHeight - 6) + 'px';
}
function hideTip() {
tip.hidden = true;
}
trigger.addEventListener('mouseenter', showTip);
trigger.addEventListener('focus', showTip);
trigger.addEventListener('mouseleave', hideTip);
trigger.addEventListener('blur', hideTip);
});
</script>
</body>
</html>This version uses one shared tooltip element for the whole page instead of a `::after` per trigger. `getBoundingClientRect()` reports the trigger's exact on-screen position, so the tooltip can be placed above it precisely - and because the position is computed in JavaScript rather than fixed in CSS, you could extend `showTip()` to flip the tooltip below the trigger whenever it would otherwise render off the top of the screen.
Frequently Asked Questions
- How do I create a tooltip in CSS?
- Give the trigger
position: relative, put the tooltip text in a child withposition: absolute, hide it, and reveal it on:hoverand:focus. Position it withtoporbottomplus a transform to centre it on the trigger. - How do I add an arrow to a CSS tooltip?
- Use a
::afterpseudo-element with zero width and height and a thick border on one side only. The other three borders stay transparent, which renders a small triangle you can point in any direction.