A short label that appears near an element on hover or focus. The base version needs zero JS — CSS ::before/::after plus :hover/:focus covers it.
Pseudocode
Structure
The trigger carries the tooltip text itself, usually in a data-tooltip attribute rather than a separate visible element — CSS’s content: attr(data-tooltip) reads it directly.
Style
The tooltip is a ::after pseudo-element, positioned absolute relative to the trigger (position: relative), hidden by default (opacity: 0 plus a small transform) and revealed on :hover/:focus.
A small triangle — another pseudo-element, ::before — points from the tooltip back to the trigger.
Features
Pure CSS covers hover/focus and a fade-in.
Smarter positioning (flipping to the other side when the tooltip would run off the edge of the screen) needs JS, since CSS alone can’t measure where the viewport edge actually is.
CSS-only tooltip
<div class="tooltip-row"> <button class="btn" data-tooltip="Saves your changes">Save</button> <button class="btn" data-tooltip="Permanently deletes this item">Delete</button> <button class="btn" data-tooltip="Copies a link to this page">Share</button></div>
CSS alone can’t tell whether the tooltip is about to run off the edge of the screen — that needs a real measurement. This version uses one shared floating element instead of a ::after per button: on mouseenter/focus, JS measures the trigger and the tooltip with getBoundingClientRect(), prefers placing it above the button, flips it below if there isn’t room, and clamps its horizontal position so it never overflows either edge.
<div class="tooltip-row"> <button class="btn" data-tooltip="Saves your changes">Save</button> <button class="btn" data-tooltip="Permanently deletes this item — long enough to force a flip">Delete</button> <button class="btn" data-tooltip="Copies a link">Share</button></div><div class="tooltip-float" id="tf-tooltip" role="tooltip"></div>