Small explanatory overlay.
<div class="tooltip-demo">
<button type="button" class="tooltip-trigger" aria-describedby="tooltip-1">Save changes</button>
<span role="tooltip" id="tooltip-1" class="tooltip tooltip--top tooltip--visible" style="max-width:180px;background:#241430;">
Saves your changes to the cloud.
<span class="tooltip__arrow" style="background:#241430;" aria-hidden="true"></span>
</span>
</div>
<style>
.tooltip-demo { position: relative; min-height: 200px; display: flex; align-items: center; justify-content: center; box-sizing: border-box; }
.tooltip-trigger { font: 500 14px system-ui, sans-serif; padding: 9px 16px; border-radius: 8px; border: 1.5px solid #24143033; background: #FAF7F2; color: #241430; cursor: pointer; }
.tooltip-trigger:focus-visible { outline: 2px solid #241430; outline-offset: 2px; }
.tooltip { position: absolute; color: #FAF7F2; font: 500 13px/1.4 system-ui, sans-serif; padding: 7px 11px; border-radius: 8px; box-shadow: 0 10px 22px rgba(36,20,48,.24); opacity: 0; visibility: hidden; pointer-events: none; text-align: center; box-sizing: border-box; }
@media (prefers-reduced-motion: no-preference) {
.tooltip { transition: opacity .15s ease; }
}
.tooltip-trigger:hover + .tooltip, .tooltip-trigger:focus-visible + .tooltip, .tooltip--visible { opacity: 1; visibility: visible; }
.tooltip--top { bottom: calc(50% + 34px); left: 50%; transform: translateX(-50%); }
.tooltip--bottom { top: calc(50% + 34px); left: 50%; transform: translateX(-50%); }
.tooltip--right { left: calc(50% + 70px); top: 50%; transform: translateY(-50%); }
.tooltip__arrow { position: absolute; width: 8px; height: 8px; transform: rotate(45deg); }
.tooltip--top .tooltip__arrow { bottom: -4px; left: 50%; margin-left: -4px; }
.tooltip--bottom .tooltip__arrow { top: -4px; left: 50%; margin-left: -4px; }
.tooltip--right .tooltip__arrow { left: -4px; top: 50%; margin-top: -4px; }
</style><div class="relative min-h-[200px] flex items-center justify-center box-border">
<button type="button" aria-describedby="tooltip-tw-1" class="px-4 py-2.5 rounded-lg text-sm font-medium border-[1.5px] border-[#24143033] bg-[#FAF7F2] text-[#241430]">Save changes</button>
<span role="tooltip" id="tooltip-tw-1" class="tooltip-tw tooltip-tw--top tooltip-tw--visible absolute text-[#FAF7F2] text-[13px] leading-snug px-2.5 py-1.5 rounded-lg shadow-2xl text-center box-border" style="max-width:180px;background:#241430;">
Saves your changes to the cloud.
<span aria-hidden="true" class="tooltip-tw__arrow absolute w-2 h-2 rotate-45" style="background:#241430;"></span>
</span>
</div>
<style>
.tooltip-tw { opacity: 0; visibility: hidden; pointer-events: none; }
@media (prefers-reduced-motion: no-preference) {
.tooltip-tw { transition: opacity .15s ease; }
}
button:hover + .tooltip-tw, button:focus-visible + .tooltip-tw, .tooltip-tw--visible { opacity: 1; visibility: visible; }
.tooltip-tw--top { bottom: calc(50% + 34px); left: 50%; transform: translateX(-50%); }
.tooltip-tw--bottom { top: calc(50% + 34px); left: 50%; transform: translateX(-50%); }
.tooltip-tw--right { left: calc(50% + 70px); top: 50%; transform: translateY(-50%); }
.tooltip-tw--top .tooltip-tw__arrow { bottom: -4px; left: 50%; margin-left: -4px; }
.tooltip-tw--bottom .tooltip-tw__arrow { top: -4px; left: 50%; margin-left: -4px; }
.tooltip-tw--right .tooltip-tw__arrow { left: -4px; top: 50%; margin-top: -4px; }
</style>import { useState } from "react";
export function Tooltip({
label = "Save changes",
text = "Saves your changes to the cloud.",
placement = "top",
accent = "#241430",
width = 180,
visible: initialVisible = true,
}) {
const [hovered, setHovered] = useState(false);
const visible = hovered || initialVisible;
const posStyle = {
top: { bottom: "calc(50% + 34px)", left: "50%", transform: "translateX(-50%)" },
bottom: { top: "calc(50% + 34px)", left: "50%", transform: "translateX(-50%)" },
right: { left: "calc(50% + 70px)", top: "50%", transform: "translateY(-50%)" },
}[placement] ?? {};
const arrowStyle = {
top: { bottom: -4, left: "50%", marginLeft: -4 },
bottom: { top: -4, left: "50%", marginLeft: -4 },
right: { left: -4, top: "50%", marginTop: -4 },
}[placement] ?? {};
return (
<div style={{ position: "relative", minHeight: 200, display: "flex", alignItems: "center", justifyContent: "center", boxSizing: "border-box" }}>
<button type="button" aria-describedby="tooltip-1"
onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}
onFocus={() => setHovered(true)} onBlur={() => setHovered(false)}
style={{ font: "500 14px system-ui, sans-serif", padding: "9px 16px", borderRadius: 8,
border: "1.5px solid #24143033", background: "#FAF7F2", color: "#241430", cursor: "pointer" }}>
{label}
</button>
<span role="tooltip" id="tooltip-1" className="tooltip-bubble" style={{ position: "absolute", color: "#FAF7F2", fontSize: 13,
lineHeight: 1.4, fontWeight: 500, padding: "7px 11px", borderRadius: 8, boxShadow: "0 10px 22px rgba(36,20,48,.24)",
opacity: visible ? 1 : 0, visibility: visible ? "visible" : "hidden",
pointerEvents: "none", textAlign: "center", boxSizing: "border-box", maxWidth: width, background: accent, ...posStyle }}>
{text}
<span aria-hidden="true" style={{ position: "absolute", width: 8, height: 8, transform: "rotate(45deg)", background: accent, ...arrowStyle }} />
</span>
<style>{`
@media (prefers-reduced-motion: no-preference) {
.tooltip-bubble { transition: opacity .15s ease; }
}
`}</style>
</div>
);
}