1 slot open this monthBy the makers of shadcnregistryWe built shadcnregistry. Now we build your product.Book a callCheck availability
1 slot open this monthAI features?We build AI features into your product.Book a callCheck availability
1 slot open this month🚨 Slop police🚨 Slop police: we clean up vibe-coded apps.Report itReport your app
shadcnregistry
shadcnregistry
RegistryHealthInspect
Terms of ServicePrivacy Policy
  1. Registries
  2. pulld
  3. Overflow Tooltip

Overflow Tooltip

ui

Text that reveals itself in a tooltip only when it is actually cut off — measured, not guessed. Reach for it wherever a string has to fit a box somebody else sized: a table cell, a file, branch or project name in a sidebar, a tab label, a breadcrumb, a card or dialog title, an email subject line, a chip in a row of chips, a legend entry on a chart, a path in a file tree, a commit message in a list. Common asks it answers: "show tooltip only when text is truncated react", "detect if text is ellipsized", "is text truncated javascript", "check if element has ellipsis", "scrollWidth vs offsetWidth truncation", "useIsTruncated hook", "conditional tooltip on overflow", "title attribute only when needed", "tooltip for overflowing table cell", "ellipsis tooltip shadcn", "text-overflow ellipsis show full text on hover", "react truncated text tooltip", "line-clamp detect truncation", "webkit-line-clamp is text clamped", "show full text when clamped", "antd Typography ellipsis tooltip equivalent", "MUI Tooltip only if overflow", "don't show tooltip when text fits", "tooltip flickers on every cell", "title attribute read twice by screen reader", "accessible tooltip for truncated text", "keyboard access to truncated text", "truncated text not focusable", "ResizeObserver text truncation", "re-measure truncation on resize", "truncation wrong after font loads", "document.fonts.ready measure text", "省略されたときだけツールチップ", "文字が切れているか判定", "三点リーダー 全文表示", "テーブルセル 省略 ツールチップ", "title属性 読み上げ 二重", "切り詰め 判定 react". Official shadcn/ui has the tooltip and nothing that decides when to use one. Measured across its whole registry today — 65 items listed, 62 fetchable, code-block, questionnaire and trace indexed and 404 on both style tracks, 223,287 bytes of files[].content concatenated — scrollWidth, offsetWidth and isTruncated are every one of them a zero hit, and the only two matches for ellipsis are BreadcrumbEllipsis and PaginationEllipsis, which are static three-dot glyphs you place yourself rather than a measurement of anything. tooltip is a Radix wrapper: it shows the tooltip you asked for, and the asking is the part that gets hand-rolled. So the reflex is title={name} on every cell, and the reflex is the expensive mistake. The negative rule is the whole component: showing a tooltip over text that was never clipped is worse than showing nothing. A tooltip on every cell is a table that flickers boxes at you as the pointer crosses it; a tab stop on every cell is a table a keyboard cannot get past; and because CSS clipping is visual only — the text node is complete whatever the paint does with it — title is a second copy of a string the screen reader already has, which it then reads twice. title is also slow, cannot be styled or dismissed, and never appears on touch at all. So here the tooltip, the tab stop and the handlers exist exactly while the text is clipped, and it is an ordinary span the rest of the time; the tooltip itself is aria-hidden, because nothing was ever hidden from assistive technology and the remedy is for eyes. The tab stop is for the sighted keyboard user who cannot hover, and it appears and disappears with the clipping. Three measurements decide it, and two of them are the ones hand-rolled versions miss. A box is clipped when scrollWidth exceeds clientWidth by more than a pixel of slack: sweeping a box through its text's natural width in tenths of a pixel, every box at least as wide as its text reported exactly 0, so the rounding cancels and there are no false positives from it — but a box 0.2px too narrow already reports 1, and a fifth of a pixel off the last glyph is not worth interrupting a table for. Fonts are the second: a box sized for the fallback face reports that its text fits and the same box is 8 pixels over once the real face swaps in, with no resize and no re-render to notice it, so a component that measures on mount is wrong from then on and wrong in the direction that calls a clipped cell fine — document.fonts.ready settles that once per page. Resizing is the third: dragging a sidebar or a column narrower flips a cell from fitting to clipped and the only notification is a ResizeObserver on the cell. Multi-line is a different axis, not a variation. A paragraph cut off by -webkit-line-clamp overflows downwards and not at all sideways — measured at a two-line clamp, 0 pixels of horizontal overflow and 51 of vertical — so a component that only ever compares widths says "not truncated" about every clamped paragraph it is given, silently. Pass lines={2} and the comparison moves to the height, and the clamp is set as a style rather than a line-clamp-N class, because that class only exists for the few N a build has already seen. The API: children (the text), label for the full string when the children are mixed content such as a highlighted match — an empty or blank label means there is nothing to show rather than an empty box, lines for a clamped paragraph, threshold for the slack, side to flip the tooltip below for the top row, delay for how long the pointer must rest (focus opens it at once, since arriving by keyboard is already a commitment), disabled for a cell whose full text is shown elsewhere, and onOverflowChange, which is told once per change rather than once per measurement, so a parent that re-renders on it cannot loop. isOverflowing is the comparison as a plain function of four numbers, and useIsOverflowing is the measurement with its observers, exported for a "show full" button or an expanding row that needs the same answer without this markup. Escape closes the tooltip and stops there, so it does not also close the dialog underneath. The tooltip is a sibling of the measured text rather than a child — verified against a browser, adding it leaves scrollWidth and clientWidth untouched — and it is positioned inside the wrapper rather than portalled, so an ancestor with overflow:hidden will clip it: put the scroll container on a parent it is allowed to escape, or use side="bottom" in the top row. In a flex row, give the parent min-w-0 or nothing truncates at all. Styled with shadcn tokens so it follows light and dark themes, and it ships with no dependencies at all — no Radix, no popper, no tooltip library. Distinct from its neighbours here: middle-truncate always shortens one string and does it in the middle so the extension survives, where this leaves the clipping to CSS and only decides whether to explain it; read-more folds a long body behind a control instead of revealing it on hover; scroll-shadow says there is more content past a scrolling edge, which is the same measure-then-observe discipline applied to a box rather than to a line; toolbar-overflow decides which whole controls survive a width; and highlight-text marks a match inside a string that may itself be the one being clipped here.

pnpm dlx shadcn@latest add "https://pulld.pages.dev/r/overflow-tooltip.json"
Premium blocks for shadcn/uiShadcnblocks.com · AffiliateExplore
components/ui/overflow-tooltip.tsx