Tooltip
Category: Overlays · Tags: tooltip, hint, hover, focus, describedby
How to use it
Use for a short, non-essential hint about a control, one sentence at most. Do not put links, buttons or information people need to complete a task inside a tooltip (show it as visible help text or use a popover), and never rely on it to name an icon-only button. After copying, wire the button to its real action and keep the tooltip id unique, updating aria-describedby to match.
Accessibility
The trigger is a native <button> whose aria-describedby points at a role="tooltip" element, so the hint is read as its description whether or not it is visible. It appears on pointer hover and on keyboard focus, stays open while the pointer moves onto the tooltip itself, and Escape hides it without moving focus or the pointer, meeting WCAG 1.4.13 (content on hover or focus). Tooltip text is #ffffff on #0f172a (17.85:1); the button is #0f172a on #ffffff (17.85:1), #0f172a on #f1f5f9 on hover (16.30:1).
Source (copy-ready)
<div class="dcl-tooltip">
<span class="dcl-tooltip__wrap">
<button type="button" class="dcl-tooltip__trigger" aria-describedby="dcl-tooltip-tip">Archive project</button>
<span class="dcl-tooltip__tip" id="dcl-tooltip-tip" role="tooltip" hidden>Archived projects are read-only and can be restored for 90 days.</span>
</span>
</div>
<style>
.dcl-tooltip { font: inherit; color: #0f172a; background-color: #ffffff; padding-top: 4.5rem; }
.dcl-tooltip__wrap { position: relative; display: inline-block; }
.dcl-tooltip__trigger { font: inherit; padding: 0.5rem 1rem; border: 1px solid #94a3b8; border-radius: 6px; cursor: pointer; color: #0f172a; background-color: #ffffff; }
.dcl-tooltip__trigger:hover { color: #0f172a; background-color: #f1f5f9; }
.dcl-tooltip__trigger:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }
.dcl-tooltip__tip { position: absolute; bottom: 100%; left: 0; z-index: 20; display: block; width: 14rem; max-width: 80vw; margin-bottom: 0.4rem; padding: 0.45rem 0.65rem; border-radius: 6px; font-size: 0.875rem; line-height: 1.4; color: #ffffff; background-color: #0f172a; }
.dcl-tooltip__tip[hidden] { display: none; }
.dcl-tooltip__tip::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 0.4rem; }
</style>
<script>
(function () {
document.querySelectorAll(".dcl-tooltip").forEach(function (root) {
if (root.dataset.dclBound) { return; }
root.dataset.dclBound = "true";
var wrap = root.querySelector(".dcl-tooltip__wrap");
var trigger = root.querySelector(".dcl-tooltip__trigger");
var tip = root.querySelector(".dcl-tooltip__tip");
if (!wrap || !trigger || !tip) { return; }
var hovered = false, focused = false;
function show() { tip.hidden = false; }
function hide() { tip.hidden = true; }
// Hover covers the trigger and the tooltip itself, so the pointer can move onto it.
wrap.addEventListener("mouseenter", function () { hovered = true; show(); });
wrap.addEventListener("mouseleave", function () { hovered = false; if (!focused) { hide(); } });
trigger.addEventListener("focus", function () { focused = true; show(); });
trigger.addEventListener("blur", function () { focused = false; if (!hovered) { hide(); } });
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !tip.hidden) { hide(); }
});
});
})();
</script>
Was this useful? Send feedback.