A file chip cut off with an ellipsis that reveals the full name on hover, wiping out from the left edge. Align it to the start and move the arrow with the --arrow property.
Also known as: ellipsis tooltip css, show full text on hover, text overflow tooltip.
<span class="tip-truncated-text" tabindex="0"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/></svg> <span class="tip-truncated-text-name">board-review-final-v7-approved.pdf</span> <span class="tip-truncated-text-tip" role="tooltip" aria-hidden="true">board-review-final-v7-approved.pdf</span> </span>
.tip-truncated-text {
--c: var(--tip-color, #3b82f6);
font-size: var(--tip-size, 16px);
position: relative;
display: inline-flex;
--arrow: 1.5em;
box-sizing: border-box;
align-items: center;
gap: 0.5em;
width: 12em;
padding: 0.55em 0.8em;
border: 1px solid color-mix(in srgb, var(--c) 30%, transparent);
border-radius: 0.6em;
background: color-mix(in srgb, var(--c) 7%, transparent);
cursor: default;
}
.tip-truncated-text:focus-visible {
outline: 2px solid var(--c);
outline-offset: 2px;
}
.tip-truncated-text svg {
flex: none;
width: 1.2em;
height: 1.2em;
color: var(--c);
}
.tip-truncated-text-name {
min-width: 0;
overflow: hidden;
font: 500 0.9em/1.3 system-ui, sans-serif;
text-overflow: ellipsis;
white-space: nowrap;
}
/* the screen reader already reads the full name, so the bubble is aria-hidden */
.tip-truncated-text-tip {
position: absolute;
bottom: calc(100% + 0.6em);
left: 0;
z-index: 10;
padding: 0.45em 0.75em;
border-radius: 0.45em;
background: color-mix(in srgb, var(--c) 14%, #0f172a);
color: #fff;
font: 500 0.85em/1.3 system-ui, sans-serif;
white-space: nowrap;
opacity: 0;
visibility: hidden;
pointer-events: none;
clip-path: inset(-0.5em 100% -0.8em -0.5em);
transition: opacity 0.15s ease, clip-path 0.2s ease, visibility 0.2s;
}
.tip-truncated-text-tip::before {
content: "";
position: absolute;
top: 100%;
left: var(--arrow);
width: 0.6em;
height: 0.6em;
background: inherit;
transform: translate(-50%, -50%) rotate(45deg);
}
.tip-truncated-text:hover .tip-truncated-text-tip,
.tip-truncated-text:focus-within .tip-truncated-text-tip {
opacity: 1;
visibility: visible;
clip-path: inset(-0.5em -0.5em -0.8em -0.5em);
transition: opacity 0.15s ease, clip-path 0.45s cubic-bezier(0.65, 0, 0.35, 1), visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
.tip-truncated-text, .tip-truncated-text *, .tip-truncated-text::before, .tip-truncated-text::after, .tip-truncated-text *::before, .tip-truncated-text *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--tip-color on the element or a parent.
Currently previewing #3b82f6.--tip-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.