Tooltips
A tooltip is a small element that appears when the user moves the cursor over an element. Tooltips usually display additional information, a short explanation, or a hint about that element. In CSS, a tooltip can be built completely without JavaScript using the ::before and ::after pseudo-elements.
The most common approach is to use a data-tooltip attribute in the HTML, then let CSS display the attribute's content as the tooltip using attr(data-tooltip) on a pseudo-element. The tooltip is positioned absolutely relative to its parent element.
Transitions on opacity and transform provide a smooth animation when the tooltip appears. The tooltip arrow is created using the border trick on a second pseudo-element.
Code Example
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
font-family: Arial, sans-serif;
padding: 60px 40px;
background: #f5f5f5;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
}
/* Base Tooltip */
[data-tooltip] {
position: relative;
cursor: pointer;
}
[data-tooltip]::before,
[data-tooltip]::after {
position: absolute;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
pointer-events: none;
z-index: 1000;
}
[data-tooltip]::before {
content: attr(data-tooltip);
background: #333;
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 0.85em;
white-space: nowrap;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%) translateY(5px);
}
[data-tooltip]::after {
content: '';
border: 6px solid transparent;
border-top-color: #333;
bottom: calc(100% - 4px);
left: 50%;
transform: translateX(-50%) translateY(5px);
}
[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
/* Tooltip Bottom */
[data-tooltip-bottom]::before {
bottom: auto;
top: calc(100% + 8px);
transform: translateX(-50%) translateY(-5px);
}
[data-tooltip-bottom]::after {
border-top-color: transparent;
border-bottom-color: #333;
bottom: auto;
top: calc(100% - 4px);
transform: translateX(-50%) translateY(-5px);
}
[data-tooltip-bottom]:hover::before,
[data-tooltip-bottom]:hover::after {
transform: translateX(-50%) translateY(0);
}
/* Tooltip Left */
[data-tooltip-left]::before {
bottom: auto;
top: 50%;
right: calc(100% + 8px);
left: auto;
transform: translateY(-50%) translateX(5px);
}
[data-tooltip-left]::after {
border-top-color: transparent;
border-left-color: #333;
bottom: auto;
top: 50%;
right: calc(100% - 4px);
left: auto;
transform: translateY(-50%) translateX(5px);
}
[data-tooltip-left]:hover::before,
[data-tooltip-left]:hover::after {
transform: translateY(-50%) translateX(0);
}
/* Tooltip Right */
[data-tooltip-right]::before {
bottom: auto;
top: 50%;
left: calc(100% + 8px);
transform: translateY(-50%) translateX(-5px);
}
[data-tooltip-right]::after {
border-top-color: transparent;
border-right-color: #333;
bottom: auto;
top: 50%;
left: calc(100% - 4px);
transform: translateY(-50%) translateX(-5px);
}
[data-tooltip-right]:hover::before,
[data-tooltip-right]:hover::after {
transform: translateY(-50%) translateX(0);
}
/* Demos */
.demo-box {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
text-align: center;
width: 350px;
}
.demo-box h3 {
margin-top: 0;
color: #333;
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: #667eea;
color: white;
font-size: 1.2em;
margin: 0 5px;
transition: background 0.2s;
}
.icon-btn:hover {
background: #5a6fd6;
}
.info-link {
color: #667eea;
border-bottom: 1px dashed #667eea;
padding-bottom: 2px;
}
.form-group {
text-align: left;
margin: 15px 0;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #555;
}
.form-group input {
width: 100%;
padding: 10px 12px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 1em;
box-sizing: border-box;
transition: border-color 0.2s;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
}
.help-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 50%;
background: #ddd;
color: #666;
font-size: 0.7em;
font-weight: bold;
margin-left: 5px;
cursor: help;
}
.directions {
display: flex;
gap: 15px;
flex-wrap: wrap;
justify-content: center;
}
</style>
</head>
<body>
<div class="demo-box">
<h3>Tooltip Dasar</h3>
<p>Hover the cursor over the element below:</p>
<span data-tooltip="This is a tooltip!" class="info-link">
Hover me
</span>
</div>
<div class="demo-box">
<h3>Icon Buttons</h3>
<span data-tooltip="Share" class="icon-btn">📤</span>
<span data-tooltip="Sukai" class="icon-btn">❤️</span>
<span data-tooltip="Komentar" class="icon-btn">💬</span>
<span data-tooltip="Save" class="icon-btn">🔖</span>
</div>
<div class="demo-box">
<h3>Form with Tooltips</h3>
<div class="form-group">
<label>Email <span class="help-icon" data-tooltip="We will not share your email">?</span></label>
<input type="email" placeholder="[email protected]">
</div>
<div class="form-group">
<label>Password <span class="help-icon" data-tooltip="At least 8 characters, must include letters and digits">?</span></label>
<input type="password" placeholder="Enter password">
</div>
</div>
<div class="demo-box">
<h3>Tooltip Direction</h3>
<div class="directions">
<span data-tooltip="Top" class="icon-btn">↑</span>
<span data-tooltip-bottom="Bottom" class="icon-btn">↓</span>
<span data-tooltip-left="Left" class="icon-btn">←</span>
<span data-tooltip-right="Right" class="icon-btn">→</span>
</div>
</div>
</body>
</html>
Code Explanation
[data-tooltip]::before: The before pseudo-element displays the tooltip text from the data-tooltip attribute using attr(data-tooltip). This allows every element to have its own tooltip without duplicating HTML.
[data-tooltip]::after: The after pseudo-element creates the arrow of the tooltip using the border trick. The border size determines the arrow size.
position: absolute: The tooltip is positioned relative to the parent that has position: relative. This ensures the tooltip appears in the correct position.
opacity and visibility transitions: Transitions on opacity and visibility provide a smooth animation as the tooltip appears and disappears. Transform adds a subtle sliding effect.
pointer-events: none: Prevents the tooltip from interfering with mouse interaction on the element. Without this, the tooltip could block hover in certain areas.
data-tooltip-bottom/left/right: Modifiers that change the direction of the tooltip. Each direction requires adjustments to the position and the arrow's direction.
Everyday Use Cases
Icon navigation: Every icon in a navbar can have a tooltip explaining its function, especially for icons that have no text label.
Form help: A form field can have a tooltip with hints about the expected input format, such as password requirements.
Data table: A table column header can have a tooltip explaining the meaning of an abbreviation or the metric being used.
Toolbar: Buttons in an editor toolbar can have tooltips explaining keyboard shortcuts.
Conclusion
CSS tooltips are built with the ::before and ::after pseudo-elements plus a data-tooltip attribute. Transitions on opacity and transform provide the animation. The arrow is created with the border trick. This approach is pure CSS without JavaScript — lightweight and easy to maintain. For more complex tooltips with HTML formatting, consider a library or a JavaScript approach.