/

Tooltips, Popovers and Hover Cards: Three Contracts

One rectangle, three behaviours. Which is allowed to hold content, which must survive touch, and the positioning bugs all three share.

Serafim Korablev
Serafim Korablev
@korablev

Tooltip, popover and hover card look like three names for the same rectangle. They are three different contracts, and using the wrong one produces the two most common floating-element bugs on the web: content that is unreachable on a phone, and content a screen reader never hears.

446 components on 21st carry these tags. The distinction is worth more than any of them.

Tooltip: a label, nothing else

A tooltip names the thing under the cursor. It holds a few words, it has no links, no buttons and no formatting, and it appears on hover and on focus.

The rules follow from that. It must open on keyboard focus, not only on hover, or every icon button in your toolbar is anonymous to keyboard users. It is attached with aria-describedby, so it is read with the control rather than announced separately. And it must be dismissible with Escape while the pointer is still over the trigger, which is a specific WCAG requirement most implementations miss.

The important limitation: a tooltip does not exist on touch. There is no hover, and the tap goes to the button. If the information is necessary rather than supplementary, it cannot live in a tooltip.

Animated Tooltip by Manu Arora is the avatar-and-name pattern, and Animated Tooltip by ShadcnSpace is another take.

Popover: a small surface with things in it

A popover holds content: a form, a menu, a set of controls. It opens on click, never on hover, and it is dismissed by clicking outside, pressing Escape, or choosing something inside.

Popover by Efferd is the base, Morphing Popover by ibelick is the animated version where the trigger becomes the panel, Popover Form by cult-ui is the short-form case, and Quick Tooltip Actions by Suraj Gaud is the action-bar variant.

The rule that decides between a popover and a dialog: a popover must not contain anything the reader has to complete. If losing what is inside it by clicking elsewhere would be annoying, it wanted to be a dialog, and the dialog guide has that contract.

Hover card: a preview

The third case, and the one people forget exists. A hover card is a rich preview of something behind a link: a profile, a repository, an article. It opens on hover with a deliberate delay, it can contain links, and it is never the only route to the information.

That last clause is the whole design constraint. Everything in a hover card must also be reachable by following the link, because on touch the card never appears.

The positioning problems, which are the same for all three

Clipping. A floating panel inside a container with overflow: hidden, or inside a transformed ancestor, is cut off. The fix is a portal to the body, which every serious primitive does by default.

Collision. Near the bottom or the right edge, a fixed-direction panel opens off screen. Flip and shift behaviour belongs to a positioning library rather than to a top: 100% rule.

Scroll. Inside a scrolling container the panel drifts away from its trigger unless it is repositioned. Decide: reposition, or close on scroll. Doing neither is what produces a panel floating over unrelated content.

Two more that are specific to hover. Give the panel a small close delay or a safe triangle between trigger and content, or a diagonal mouse path loses it before it can be reached. And give hover-open panels 100 to 200ms of intent, so panels do not flash as the pointer crosses a toolbar.

Where else to look

The honest list, because the answer is not always us:

SourceBest forTrade-off
shadcn/uiTooltip, Popover and HoverCard as three distinct componentsPlain by design
Radix / Base UIThe behaviour underneath: portals, collisions, delays, dismissalUnstyled
Floating UIPositioning alone: flip, shift, arrow, sizeNot a component
The popover attributeNative top-layer popovers with no JavaScriptNewer, and the positioning story is still settling
21stAnimated and morphing variants of all threeQuality varies by author, so preview before you take it

Taking one

Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:

bash

That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue. The 21st MCP covers the same ground without leaving the editor: ask Claude, Cursor or Codex for a tooltip, get the previews inline, and let the agent write the file.

Browse tooltip components →

Frequently asked

What is the difference between a tooltip and a popover?
A tooltip is a label: a few words, no links, no buttons, opening on hover and on focus, attached with aria-describedby. A popover holds content and opens on click, never on hover. If losing what is inside by clicking elsewhere would annoy the reader, it wanted to be a dialog instead.
Do tooltips work on mobile?
No. There is no hover on touch and the tap goes to the button, so anything necessary cannot live in a tooltip. Supplementary labels are fine; instructions, error explanations and anything the reader must read are not, and need to be visible or in a popover opened by a real control.
What accessibility rules apply to tooltips?
They must open on keyboard focus and not only on hover, or every icon button is anonymous to keyboard users. They must be dismissible with Escape while the pointer is still over the trigger, which is a specific WCAG requirement most implementations miss. And they attach with aria-describedby so they are read with the control.
Why is my popover cut off or in the wrong place?
Three usual causes: an ancestor with overflow hidden or a transform clips it, which a portal fixes; a fixed-direction panel opens off screen near a viewport edge, which needs collision-aware positioning; and inside a scroll container it drifts unless repositioned or closed on scroll.

Published

Aug 20, 2026

Read time

5 min

Tags

GuideOverlaysReactAccessibility

Share