返回博客

Tailwind Preflight 让 docx-preview 浮动图片消失的真相

把一个跑了两年的 vanilla JS 合同模板编辑器迁移到 Next.js + Tailwind 栈,文字、表格渲染完全一致,唯独公司 Logo 和印章全部消失了。检查 renderAsync 配置没问题,JSZip 拆包也正常,blob URL 能打开……直到我在 DevTools 里盯着那张图片的 clientWidth: 0 发呆了三分钟,才想到去看父节点。

现象

  • Word 文档的正文、表格、页眉页脚渲染完全正常。
  • 浮动图片(Word 里的「嵌入型之外」的图片:Logo、水印、印章、签名图)整块消失
  • DOM 里 <img> 节点存在,src 是合法的 blob: URL,naturalWidth / naturalHeight 均有值,complete === true——图片文件本身没有问题。
  • clientWidth === 0getBoundingClientRect().width === 0
// DevTools 探针
const img = document.querySelector('.docx img')
console.log({
  natW: img.naturalWidth,   // 693 — 有值
  clientW: img.clientWidth, // 0   — 消失
  complete: img.complete,   // true
})

根因

Tailwind v3(@tailwind base)和 v4(@import 'tailwindcss')的 preflight 都包含:

img, video {
  max-width: 100%;
  height: auto;
}

docx-preview 对 Word 里的浮动图形使用一个「零尺寸锚点」父容器来定位:

<div style="display: block; position: relative; width: 0px; height: 0px; left: -3.25pt; top: -8.25pt;">
  <img src="blob:…" style="position: relative; left: 0pt; top: 0pt; width: 37.45pt; height: 31.2pt;">
</div>

父容器的 width: 0px 是故意的——它只是一个定位锚点,图片自己通过内联 width: 37.45pt 跳出正常文档流渲染。在纯 CSS 环境下,内联 width 正常生效,图片按 37.45pt 宽度显示。

引入 Tailwind preflight 后,max-width: 100% 相对父容器解析为 max-width: 0px。CSS 规范里 max-width 优先级高于 width,结果:内联 width: 37.45pt 被截死,图片宽度归零

坑:max-width: 100% 遇到零宽父容器直接归零

preflight 的 max-width: 100% 是相对于包含块(父容器)解析的。浮动图片的锚点父容器刻意是 width: 0,于是 100% = 0,图片内联的正向 width 被完全覆盖。

症状很像图片资源加载失败,但 naturalWidth > 0 + complete === true 已经排除了网络问题;真正的凶手隐藏在 CSS 层叠顺序里。

正解

在 doc 渲染容器的作用域内覆盖掉 preflight:

/* globals.css 或对应的组件样式 */
 
/* docx-preview 的浮动图片父容器 width: 0,
   Tailwind preflight 的 max-width: 100% 在此会解析为 0,
   需要在 doc 表面作用域内恢复内联尺寸。 */
.docx-wrapper img,
.docx img {
  max-width: none;
  height: auto;
}

如果你给 doc 渲染容器加了自己的包裹类,用那个类收窄选择器更精确:

.template-doc-surface img {
  max-width: none;
  height: auto;
}
正解:作用域内覆盖,不要全局关闭 preflight

一定要限定在 doc viewer 容器内,不要全局写 img { max-width: none }——其他地方的头像、缩略图、卡片封面依赖 max-width: 100% 才能在弹性布局里正常收缩,全局关掉会引入一批新问题。

height: auto 也要一并写上:preflight 同样设置了 height: auto,如果图片内联同时指定了 widthheight,只覆盖 max-width 而不覆盖 height,宽度恢复后高度仍受 auto 影响,会造成比例失真。

排查捷径

碰到 docx / Word 文档渲染图片消失,先做这三步,不要动 renderAsync 配置

  1. DevTools 选中消失区域,确认 <img> 节点存在且 src 有效。
  2. 检查 img.clientWidth——若为 0 但 naturalWidth > 0,立即看父容器 width
  3. 父容器 width: 0px + 项目用了 Tailwind / shadcn → 本文的坑,五分钟内可修。

一句话外卖

Tailwind preflight 的 max-width: 100% 会被零宽父容器截死——第三方文档渲染库里大量使用「零宽锚点」做浮动定位,迁移到 Tailwind 栈后务必在 doc viewer 容器内用 max-width: none 局部覆盖,而不是全局关掉 preflight。