Truncated text
One line that ends in an ellipsis when it runs out of room. The full text shows in a tooltip, and only while the line is clipped.
Playground
Change the controls to try each prop. The preview is a 220px table cell. The code under it updates as you go and lists only the props you changed; copy it into your page.
import { TruncatedText } from '@/ui/truncated-text';<TruncatedText text="Northbridge Talent Partners International" />Clipped
Hover or Tab to the name. The ellipsis says there is more; the tooltip opens after 300ms with the whole name. A clipped line joins the tab order, so keyboard focus opens the same tooltip.
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextClipped() {
return (
<div className="w-[220px] rounded-medium bg-surface px-100 py-050 text-body leading-body text-text shadow-control">
<TruncatedText text="Northbridge Talent Partners International" />
</div>
);
}Fits
Nothing is hidden, so there is no tooltip and no tab stop. A tooltip that repeats what's on screen is noise.
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextFits() {
return (
<div className="w-[220px] rounded-medium bg-surface px-100 py-050 text-body leading-body text-text shadow-control">
<TruncatedText text="Halcyon Bank" />
</div>
);
}Always
With always, the tooltip adds detail on every hover and focus: the exact time behind a relative date. children is what the line shows; text is the tooltip. Screen readers hear both.
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextAlways() {
return (
<div className="flex text-body leading-body text-text">
<TruncatedText text="Sent 14 Oct 2026, 09:42" always tone="subtle">
Yesterday
</TruncatedText>
</div>
);
}Subtle tone
tone="subtle" takes the subtle text colour, for a secondary value under a name.
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextSubtle() {
return (
<div className="flex w-[220px] flex-col rounded-medium bg-surface px-100 py-050 text-body leading-body shadow-control">
<TruncatedText text="Maya Chen" className="font-medium text-text" />
<TruncatedText text="Halcyon Bank, Treasury Operations" tone="subtle" />
</div>
);
}Where it truncates
Row list
The name gives way first; the avatar and the status keep their width.
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextRowList() {
return (
<div className="flex w-[260px] items-center gap-100 rounded-medium bg-surface px-100 py-075 text-body leading-body text-text shadow-control">
<span aria-hidden className="flex size-6 shrink-0 items-center justify-center rounded-full bg-tile text-body-small leading-body-small font-medium text-text-subtle">
MC
</span>
<TruncatedText text="Maya Chen-Abernathy" className="flex-1 font-medium" />
<span className="shrink-0 rounded-medium bg-background-current px-075 text-body-small leading-body-small font-medium text-text-subtle">
Interview
</span>
</div>
);
}Details rail
The value clips at the rail edge; the label never does.
- Job
- Senior Data Engineer, Payments Platform
import { TruncatedText } from '@/ui/truncated-text';
export default function TruncatedTextDetailsRail() {
return (
<dl className="flex w-[240px] items-baseline gap-150 rounded-medium bg-surface px-150 py-100 text-body leading-body shadow-control">
<dt className="shrink-0 text-body-small leading-body-small font-medium text-text-subtle">Job</dt>
<dd className="min-w-0 flex-1 text-text">
<TruncatedText text="Senior Data Engineer, Payments Platform" />
</dd>
</dl>
);
}Tag group
Tags on one line: as many as fit, then +N with the rest in a tooltip. The row never wraps and never grows.
Value diff
A field's value before and after a change: the old value struck through, an arrow, the new one. Values draw as the table draws them, so a stage stays a pill and money stays money.