HeroUI Tooltip.Trigger 的 inline-block 悄悄击穿了 flex 省略号
做一个带侧边栏的管理界面,左侧列表展示若干条目名称。名称可能很长,早已按标准 flex 省略号三件套写好:flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap。本地跑起来效果完美,截断干净利落。
产品说要加 Tooltip,鼠标悬停时显示完整名称。加完之后,省略号消失了——长名称直接把侧边栏撑出了横向滚动条。
现象
- 不套
<Tooltip>时,列表行省略号正常。 - 套上
<Tooltip><Tooltip.Trigger>{row}</Tooltip.Trigger>…</Tooltip>之后,某些长名称行宽度飙升,侧边栏出现横向溢出。 min-width:0已经沿 flex 链逐层加到位,删掉 Tooltip 立刻恢复正常。- 浏览器 DevTools 检查发现:溢出元素是一个 class 为
tooltip__trigger的包装 span,它的offsetWidth等于文字的自然宽度(nowrap 全展开宽),而不是容器宽度。
根因
HeroUI v3 的 Tooltip.Trigger 不是 Radix UI 那种将 props merge 到子元素的 Slot——它会在 DOM 里插入一个真实的包装元素(<span class="tooltip__trigger" data-slot="trigger">),并且该元素默认 display: inline-block。
inline-block 的盒模型是 shrink-to-fit:宽度等于内容的固有宽度(即 white-space: nowrap 下文字的完整长度),而不受父容器约束。于是:
- 父 flex 容器把可用宽度分配给
tooltip__trigger; - 但
tooltip__trigger是 inline-block,它自行扩张到内容宽度; - 里面的
.row撑满了这个"更宽的"包装,min-width:0的约束根本来不及生效; text-overflow: ellipsis的触发前提(容器宽度 < 内容宽度)永远不成立——省略号失效。
flex 布局中的省略号依赖「可用宽度 → min-width:0 → overflow:hidden → ellipsis」这条完整链路。任何中间插入的 display: inline-block 元素都会在链路中打开一个缺口:它以内容宽度为准自行膨胀,让下游的收缩约束全部失效。HeroUI Tooltip.Trigger 默认就是 inline-block,这个坑只有在长文本场景下才会显现。
正解
用 CSS 把触发器包装元素强制改为 block 并加上 min-width:0。用 :has() 把范围限制在"包裹列表行"的触发器上,避免影响按钮、图标类的 inline 触发器:
/* 仅对包含列表行的触发器生效,按钮/图标触发器保持 inline */
.tooltip__trigger:has(> .list-row),
.tooltip__trigger:has(> .dir-path) {
display: block;
min-width: 0;
}同时确认完整的 flex 收缩链:
/* 侧边栏 → 列表容器 → 触发器(已 override 为 block)→ 行 → 文字 */
.sidebar { display: flex; flex-direction: column; min-width: 0; }
.list-row { display: flex; align-items: center; min-width: 0; }
.list-row .name { flex: 1; min-width: 0; overflow: hidden;
text-overflow: ellipsis; white-space: nowrap; }如果不想依赖组件库的内部 class 名,也可以改用 [data-slot="trigger"] 选择器,或在 Tooltip.Trigger 内部额外套一层 <div style="min-width:0"> 作为隔离层。
给 .tooltip__trigger 加 display: block; min-width: 0(配合 :has() 限制作用域),让包装元素重新参与 flex 收缩,省略号立即恢复,Tooltip 悬浮功能不受影响。
一句话外卖
flex 省略号失效先查 flex 链路中有没有 inline-block 中间层——任何组件库注入的包装元素都可能是它,HeroUI Tooltip.Trigger 是一个典型案例。