返回博客

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 下文字的完整长度),而不受父容器约束。于是:

  1. 父 flex 容器把可用宽度分配给 tooltip__trigger
  2. tooltip__trigger 是 inline-block,它自行扩张到内容宽度;
  3. 里面的 .row 撑满了这个"更宽的"包装,min-width:0 的约束根本来不及生效;
  4. text-overflow: ellipsis 的触发前提(容器宽度 < 内容宽度)永远不成立——省略号失效。
坑:inline-block 包装器击穿 flex 收缩链

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"> 作为隔离层。

正解:display:block + min-width:0 接回断裂的收缩链

.tooltip__triggerdisplay: block; min-width: 0(配合 :has() 限制作用域),让包装元素重新参与 flex 收缩,省略号立即恢复,Tooltip 悬浮功能不受影响。

一句话外卖

flex 省略号失效先查 flex 链路中有没有 inline-block 中间层——任何组件库注入的包装元素都可能是它,HeroUI Tooltip.Trigger 是一个典型案例。