Open any tooltip, copy the HTML and CSS, and swap in your own trigger and text. The tooltip is a child of the trigger's wrapper, so it positions itself with position: absolute and needs no JavaScript. It appears on :hover and on :focus-within, so keyboard users get it too.
The tooltip text is real text: link it to its trigger with aria-describedby (the snippets do) so screen readers announce it. Keep tooltips short, and never hide essential information in one, since touch screens have no hover.
To change the color, set --tip-color; to change the size, set --tip-size. Every snippet ships with a prefers-reduced-motion rule that shows and hides the tooltip without motion.
Wrap the trigger and the tooltip in one element with position: relative, place the tooltip with position: absolute, hide it with opacity: 0, and reveal it on :hover and :focus-within.
Draw a small square with a ::before pseudo-element, rotate it 45 degrees and tuck it half under the tooltip's edge, or use a CSS triangle made of borders. Each entry above shows the arrow for its side.
Transition opacity together with a small transform (translate or scale from the trigger side). Keep it under 200ms so it feels responsive.
They can be: show them on focus as well as hover, connect them with aria-describedby, give them role="tooltip", and let Escape or moving away dismiss them.
The native title tooltip can't be styled, appears after a delay and never shows on keyboard focus or touch. A CSS tooltip fixes all three.